目录
1.关于模版的介绍
2.函数模版
2.1函数模板概念
2.2函数模板格式
2.3 函数模板的原理
2.4 函数模板的实例化
2.5模板参数的匹配原则
3.类模版
3.1类模板的定义格式
3.2 类模板的实例化
1.关于模版的介绍
- C++中的模板是一种通用编程工具,它允许程序员编写具有通用性的函数或类,可以适用于差异类型的数据。模板利用泛型编程的概念,即代码可以处理惩罚多种差异的数据类型,而不只是特定的类型。
- 在C++中,有两种类型的模板:函数模板和类模板。
- --->函数模板是一种通用的函数定义,可以用于处理惩罚差异类型的参数。函数模板利用一对尖括号 "<>" 困绕一个或多个类型参数,这些类型参数在函数定义中可以用作函数参数或返回类型的占位符。在调用函数模板时,编译器会根据实际参数的类型来推导出模板参数的详细类型。
- --->类模板是一种通用的类定义,可以用于创建具有相同结构但大概利用差异类型的成员的类。类模板的定义利用一对尖括号 "<>" 困绕一个或多个类型参数,这些类型参数在类定义中可以用作类的成员变量、成员函数的参数或返回类型的占位符。在实例化类模板时,需要提供实际类型的参数。
- 利用模板可以提供代码的重用性和机动性,因为它使得编写可以用于处理惩罚差异类型数据的通用代码成为大概。模板还可以提供更好的类型查抄,并在编译时进行错误查抄。
2.函数模版
2.1函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在利用时被参数化,根据实参类型产生函数的特定类型版本。 2.2函数模板格式
template<typename T1, typename T2,......,typename Tn>返回值类型 函数名(参数列表){} 演示: - template<typename T>//template<class T>
- void Swap(T& a, T& b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
- int main()
- {
- int a = 1, b = 0; double c = 3.1415925, d = 4.2526036;
- Swap(a, b); Swap(c, d);
- cout << a << b << c << d << endl;
- return 0;
- }
复制代码 2.3 函数模板的原理
函数模板是一个蓝图,它本身并不是函数,是编译器用利用方式产生特定详细类型函数的模具。所以实在模板就是将本来应该我们做的重复的事情交给了编译器。 在编译器编译阶段,对于模板函数的利用,编译器需要根据传入的实参类型来推演天生对应类型的函数以供调用。比如:当用double类型利用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理惩罚double类型的代码,对于字符类型也是如此。 演示: - template<typename T>//template<class T>
- void Swap(T& a, T& b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
- int main()
- {
- int a = 1, b = 0;
- Swap(a, b);
- double c = 3.1415925, d = 4.2526036;
- Swap(c, d);
- char e = 'a', f = 'b';
- Swap(e, f);
- return 0;
- }
复制代码
2.4 函数模板的实例化
用差异类型的参数利用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。 1. 隐式实例化:让编译器根据实参推演模板参数的实际类型 演示: - template<typename T1,typename T2>//template<class T>
- void Swap(T1& a, T2& b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
- int main()
- {
- int a = 1, b = 0;
- double c = 3.1415925, d = 4.2526036;
- Swap(a, b);
- Swap(c, d);
- Swap(a, d);
- return 0;
- }
复制代码 这段代码可以成功运行吗?固然可以,因为我们用typename创建了两个模版参数T1和T2,传参时会发生隐式实例化,即便是a与d参数类型差异,也可以分别实例化,那如果将参数减少为一个呢?
- // 专门处理int的加法函数
- int Add(int left, int right)
- {
- return left + right;
- }
- // 通用加法函数
- template<class T>
- T Add(T left, T right)
- {
- return left + right;
- }
- void Test()
- {
- Add(1, 2); // 与非模板函数匹配,编译器不需要特化
- Add<int>(1, 2); // 调用编译器特化的Add版本
- }
复制代码
这时候报错了,这是因为a是int类型,d是double类型,如果都交给同一个模版参数那么编译器就不知道要隐式实例化为int还是double类型,这时有两种解决办法:
- 将a欺压类型转换为double或将d欺压类型转换为int;
- 利用显式实例化
2. 显式实例化:在函数名后的<>中指定模板参数的实际类型 - template<typename V>//template<class T>
- void Swap(const V& a,const V& b)
- {
- V tmp = a;
- a = b;
- b = tmp;
- }
- int main()
- {
- int a = 1, b = 0;
- double c = 3.1415925, d = 4.2526036;
- Swap<double>(a, d);
- return 0;
- }
复制代码 注意:这里只是演示显式实例化,交换函数在传参时会产生临时变量,具有常性,因此必须用const类型进行接收,否则权限就被放大了,但const修饰之后不可以更改值,因此还请读者注意!
2.5模板参数的匹配原则
1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数 - // 专门处理int的加法函数
- int Add(int left, int right)
- {
- return left + right;
- }
- // 通用加法函数
- template<class T>
- T Add(T left, T right)
- {
- return left + right;
- }
- void Test()
- {
- Add(1, 2); // 与非模板函数匹配,编译器不需要特化
- Add<int>(1, 2); // 调用编译器特化的Add版本
- }
复制代码 2. 对于非模板函数和同名函数模板,如果其他条件都相同,在变更时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板 - // 专门处理int的加法函数
- int Add(int left, int right)
- {
- return left + right;
- }
- template<class T1, class T2>
- T1 Add(T1 left, T2 right)
- {
- return left + right;
- }
- void Test()
- {
- Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
- Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函
- 数
- }
复制代码 3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换 3.类模版
3.1类模板的定义格式
情势:
- template<class T1, class T2, ..., class Tn>
- class 类模板名
- {
- // 类内成员定义
- };
复制代码 演示:
- template<class T>
- class A
- {
- public:
- A(int year = 2025, int month = 1, int day = 19);
- ~A()
- {}
- private:
- T* arr;
- int _year;
- int _month;
- int _day;
- };
- template<class T>//作用域就是一个函数或者就是一个类,提取出来就要加上模板
- A<T>::A(int year, int month , int day)//A是类名 A<T>是类型 要区分开来
- {
- cout << year << "-" << month << "-" << day << endl;
- this->_year = year;
- this->_month = month;
- this->_day = day;
- }
复制代码 注意:
- 缺省参数只能在声明时候标注,在定义的时候标注会引发冲突;
- 一般在类中模版参数类型为class,而普通函数模版参数类型为typename;
- 类中函数在类外定义时,需要加上template...因为模版的作用域是一个类或者是一个函数,每当重新定义新的类或者函数,都需要重新加上模版
- 类外函数声明在用模版参数时不再是简单的类名::函数名,而需要在类名后加上<模版参数>,这是语法规定!
3.2 类模板的实例化
类模板实例化与函数模板实例化差异,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的效果才是真正的类。
演示: - template<class T>
- class A
- {
- public:
- A(int year = 2025, int month = 1, int day = 19);
- ~A()
- {}
- private:
- T* arr;
- int _year;
- int _month;
- int _day;
- };
- template<class T>//作用域就是一个函数或者就是一个类,提取出来就要加上模板
- A<T>::A(int year, int month , int day)//A是类名 A<T>是类型 要区分开来
- {
- cout << year << "-" << month << "-" << day << endl;
- this->_year = year;
- this->_month = month;
- this->_day = day;
- }int main(){ A<int> a1; A<double> a2; return 0;}
复制代码 在main函数中实例化一个类时,利用<>,中间填入模版类型即可~
补充:
- 模板的实参在任何时候都可以省略,模板实参省略意思为隐式实例化,一般情况下都利用隐式实例化,不需指定模板类型参数,让编译器进行推导,但有些情况下编译器推导时大概会有歧义,比如:模板参数只有一个类型T,但是用两个差异类型隐式实例化。
- 类模板与模板类所指的是同一概念,类模板是一个类家族,模板类是通过类模板实例化的详细类
- 类模板的参数可以是假造类型的,也可以是详细类型,C++中类模板的参数即为模板参数列表中内容,有两种方式:类型参数和非类型参数,类型参数:即类型参数化,未来实例化为详细的实际类型,有点像函数的形参,形参可以接受差异值的实参;非类型参数:在定义时给定了详细的类型,用该类型定义的为常量。
- 类模板中的成员函数满是模板函数,定义时都必须通过完备的模板语法进行定义。 因为所有类模板的成员函数,放在类外定义时,需要在函数名前加类名,而类名实际为ClassName<T>,所以定义时还需加模板参数列表。
- - - - - - ————————————本文结束———————————— - - - - - -
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |