C++核心编程--模板的使用

模板的使用

1、C++函数模板

项目需求: 实现多个函数用来返回两个数的最大值,要求能支持char类型、int类型、double

//template关键字告诉C++编译器 我要开始泛型编程了,请你不要随意报错
//T - 参数化数据类型
template <typename T>

T Max(T a,T b){
	return a > b ? a : b;
}
void main()
{
	//char a = 'c';

	int  x = 1;
	int	 y = 2;
	cout << "max(1, 2) = " << Max(x, y) << endl;      //实现参数类型的自动推导
	cout << "max(1, 2) = " << Max<int>(x, y) << endl; //显示类型调用

	float a = 2.0;
	float b = 3.0;

	cout << "max(2.0, 3.0) = " << Max(a, b) << endl;

	system("pause");
	return;
}

函数模板:实际上时建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。

定义形式:模板说明+函数定义+函数模板调用

template <类型形式参数表>
类型 函数名(形式参数表)   //形式参数表 要和 类型形式参数表一样
{
	//语句序列
}
类型形式参数表的形式: typename T,typename T1,...,typename Tn 或 class T,class T1,...,class Tn
形式参数表:T a, T1 b, ... ,Tn z;
函数模板的调用:
    max<int>(a,b); //显示类型调用
	max(a,b);	   //自动数据类型推导

模板函数:编译生成的模板函数

1、函数模板和函数重载

  • 两者允许并存
  • 函数模板不允许自动类型转化
  • 普通函数能够进行自动类型转换
#include <iostream>

using namespace std;

template <typename T1,typename T2>
void Swap(T1& a, T2& b) {
	T1 t;
	t = a;
	a = b;
	b = t;
	cout << "Swap 模板函数被调用了" << endl;
}

void Swap(char& a, int& b) {
	int t;
	t = a;
	a = b;
	b = t;
	cout << "Swap 普通函数被调用了" << endl;
}

int main(void) {
	char cNum = 'c';
	int iNum = 65;

	//第一种情况 模板函数和普通函数并存,参数类型和普通重载函数更匹配
	//调用普通函数
	Swap(cNum, iNum);//Swap 普通函数被调用了

	//第二种类型 不存在普通函数,函数模板会隐式数据类型转换嘛?
	//结论是:不提供隐式的数据类型转换,必须是严格的匹配
	Swap(cNum, iNum);
	
    //当函数模板 和 普通函数都符合调用时,优先选择普通函数
    //如果显式的使用函数模板,则使用<>类型列表
    Max<>(a,b);
    
	system("pause");
	return 0;
}

2、函数模板可以嵌套使用

template <typename T>
T Max(T a,T b){
    cout<<"调用 T Max(T a,T b)"<<endl;
    return a > b ? a : b;
}
template<typename T>
T Max(T a,T b,T c){
    cout<<"调用 T Max(T a, T b, T c)"<<endl;
    return Max(Max(a,b),c);
}

函数模板的调用机制

  1. 编译器并不是把函数模板处理成能够处理任意类型的函数
  2. 编译器从函数模板通过具体类型产生不同的函数

2、类模板的使用

语法规则

#include <iostream>

using namespace std;

template <typename T>

class A {
public:
	//函数参数列表使用虚拟类型
	A(T t = 0 ) {
		this->t = t;
	}
	//成员函数返回值使用虚拟类型
	T getT() {
		return t;
	}
private:
	//成员变量使用虚拟类型
	T t;
};
//模板类作为参数列表
void PrintA(A<int> &a){
    cout<<a.getT()<<endl;
}
int main(void) {
	//1、类模板定义类对象,必须显示指定类型
	//2、模板中如果使用了构造函数,则遵守以前的类的构造函数的调用规则
	A<int> a(66);
	cout << a.getT() << endl;
	
    PrintA(a);
	system("pause");

	return 0;
}

1、类模板和继承

  • 继承中父子类和模板类的结合情况
    • 1、父类是一般类,子类是模板类 和普通继承的用法类似
    • 2、子类是一般类,父类是模板类 在继承时必须在子类里实例化父类的类型参数
    • 3、子类父类都是模板类 把上面两个要求都做到
#include <iostream>

using namespace std;

//class B {
//public:
//	B(int t) {
//		this->b = t;
//	}
//private:
//	int b;
//};

template <typename T>

class A {
public:
	//函数参数列表使用虚拟类型
	A(T t = 0 ){
		this->t = t;
	}
	//成员函数返回值使用虚拟类型
	T getT() {
		return t;
	}
private:
	//成员变量使用虚拟类型
	T t;
};

class B :public A<int> {
public:
	B(int t):A<int>(666) {
		this->b = t;
	}
private:
	int b;
};
int main(void) {
	//1、类模板定义类对象,必须显示指定类型
	//2、模板中如果使用了构造函数,则遵守以前的类的构造函数的调用规则
	A<int> a(66);
	cout << a.getT() << endl;

	system("pause");

	return 0;
}
2、类模板的三种写法
  1. 所有的类模板函数写在类的内部

  2. 所有的类模板函数写在类的外部,在一个cpp

    1. #include <iostream>
      
      using namespace std;
      
      
      template <typename T>
      class A {
      public:
      	A(T t = 0);
      	T getT();
      	A operator + (const A& other);
      	void print();
      private:
      	T t;
      };
      
      template<typename T>
      A<T>::A(T t)
      {
      	this->t = t;
      }
      
      template<typename T>
      T A<T>::getT()
      {
      	return t;
      }
      
      template<typename T>
      A<T> A<T>::operator+(const A<T> & other) {
      	A<T> tmp;//类的内部类型可以显示声明也可以不显示
      	tmp.t = this->t + other.t;
      	return tmp;
      }
      template<typename T>
      void  A<T>::print() {
      	cout << this->t << endl;
      }
      
      int main(void) {
      	
      	A<int> a(666), b(888);
      
      	A<int> tmp = a + b;
      
      	tmp.print();//1554
      
      	system("pause");
      
      	return 0;
      }
      
    2. 在同一个cpp文件中,把模板类的成员函数放到类的外部,需要以下几点:

      • 函数声明 template <类型形式参数表>
      • 类的成员函数前的类限定域说明必须带上虚拟参数列表
      • 返回的变量是模板类的对象时必须带上虚拟参数列表
      • 成员函数参数中出现模板类的对象时必须带上虚拟参数列表
      • 成员函数内部没有限定
  3. 所有的类模板函数写在类的外部,在不同的.h和.cpp

    1. demo.h

      1. template <typename T>
        class A
        {
        public:
        	A(T t = 0);
            T &getT();
            A operator + (const A &other);
            void print();
        private:
            T t;
        }
        
    2. demo.cpp

      1. #include "demo.h"
        #include <iostream>
        
        template<typename T>
        A<T>::A(T t)
        {
        	this->t = t;
        }
        
        template<typename T>
        T A<T>::getT()
        {
        	return t;
        }
        
        template<typename T>
        A<T> A<T>::operator+(const A<T> & other) {
        	A<T> tmp;//类的内部类型可以显示声明也可以不显示
        	tmp.t = this->t + other.t;
        	return tmp;
        }
        template<typename T>
        void  A<T>::print() {
        	cout << this->t << endl;
        }
        
        
  4. 特殊情况 友元函数 少用

    1. #include <iostream>
      
      using namespace std;
      
      template <typename T>
      
      class A {
      public:
      	A(T t = 0);
      
      	//声明一个友元函数,实现对两个A类对象进行加法操作
      	template <typename T>
      	friend A<T> addA(const A<T>& a, const A<T>& b);
      
      	T getT();
      	A operator + (const A& other);
      	void print();
      private:
      	T t;
      };
      
      
      template <typename T>
      A<T>::A(T t)
      {
      	this->t = t;
      }
      
      template <typename T>
      T A<T>::getT()
      {
      	return t;
      }
      
      template <typename T>
      A<T> A<T>::operator+(const A<T> & other) {
      	A tmp;//类的内部类型可以显示声明也可以不显示
      	tmp.t = this->t + other.t;
      	return tmp;
      }
      
      template <typename T>
      void  A<T>::print() {
      	cout << this->t << endl;
      }
      //A类的友元函数
      
      template <typename T>
      A<T> addA(const A<T>& a, const A<T>& b) {
      	A<T> tmp;
      	tmp.t = a.t + b.t;
      	return tmp;
      }
      int main(void) {
      	
      	A<int> a(666), b(888);
      
      	A<int> tmp = a + b;
      	A<int> tmp1 = addA<int>(a, b);
      	tmp.print();//1554
      	tmp1.print();
      
      	system("pause");
      
      	return 0;
      }
      
      
    2. 结论:

      1. 类内部声明友元函数,必须写成以下格式

        1. template<typename T>
          friend A<T> add<T>(A<T> &a,A<T> &b);
          
      2. 友元函数实现,必须写成:

        1. template<typename T>
          A<T> add(A<T> &a,A<T> &b){
              //...
          }
          
      3. 友元函数调用必须写成

        1. A<int> c4 = add<int>(c1,c2);
          

类模和static数据成员

  • 类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员
  • 和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化
  • static数据成员也可以使用虚拟类型参数T

类模板使用总结

  1. 先写出一个实际的类

  2. 将此类中准备改变的类型名(如int 要改变为float或char)改用一个自己指定虚拟类型名

  3. 在类声明前面加入一行,格式为:

    1. template <typename 虚拟类型参数>

      如:

      template

      class A

      {…}; //类体

  4. 用类模板定义对象时,用以下形式:

    1. 类模板名<实际类型名> 对象名;

      或 类模板名<实际类型名> 对象名(实参表列);

      如:

      A cmp;

      A cmp(3,7);

  5. 如果在类模板外定义成员函数,应写成类模板形式:

    1. template<typename 虚拟类型参数>

      函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列){…}

关于类模板的几点补充

    1. 类模板的类型参数可以有一个或多个,每个类型前面都必须加typename 或class,如:
    • template <typename T1,typename T2>
      class someclass
      {};
      
  • 在定义对象时分别代入实际的类型名,如:

  • someclass<int, char> object;
    
    1. 和使用类一样,使用类模板时要注意其作用域,只有在它的有效作用域内用使用它定义对象。
    1. 模板类也可以有支持继承,有层次关系,一个类模板可以作为基类,派生出派生模板类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值