类模板和函数模板不一样,编译器不能为类模板推断模板参数。所以为了使用类模板,必须在模板名后面用尖括号“<>”提供额外信息,这些信息其实就是对应着模板参数列表里的参数。例如我们已经熟悉的vector
C++中为什么会出现类模板这个概念呢?当然这也与函数模板一个道理,一个容器,如 vector 容器 ,可以往里面放整型元素、实型元素 、字符串 ,甚至还可以装其他类对象。为了避免出现很多重复的代码,引入了类模板没然后通过模板参数,往这个类模板中传递不同的类型或者非类型参数,从而实现同一套代码,可以应付不同的数据类型,这样,代码就显得精简和通用多了。
类模板(也称为模板类)定义的一般形式如下:
- template <typename形参名1,typename形参名2,..,typename形参名n>
- class 类名
- {
- //......
- }
请注意,template后面的”<>”中如果有多个模板参数,参数之间要用逗号分隔。
用类模板定义对象的写法:
类模板名 <真实类型参数表> 对象名(构造函数实参表);
演示一下,写一个比较正规的类模板,可以考虑模拟 C++ 标准库提供的 vector ,写一个 自己的 vector ,起名为 myvector。以往实现一个类的候可以写一个 文件来进行类定义,然后再写一个. cpp 文件实现类的各种成员函数.但是对于类模板,因为实例化具体类的时候必须有类模板的全部信息,包括类模板中成员函数的函数体具体内容等,所以,类模板的所有信息,不管是声明,还是实现等内容,都必须写到一个.h文件中去,其他的要用到类模板的源程序文件(如 .cpp文件) 只要#include 这个类模板的.h文件即可。
创建一个myvector.h文件,内容文件:
- using namespace std;
-
- template<typename T>
- class myvector
- {
- public:
- typedef T* myiterator;
-
- public:
- myvector();
- myvector& operator=(const myvector&);
- public:
- myiterator mybegin();
- myiterator myend();
- };
-
- #endif // !__STUDENT_H__
在.cpp文件中这样写:
- using namespace std;
-
- template<typename T>
- myvector
::myvector()//实例化构造函数 - {
-
- }
在main主函数中,要给这个类模板用尖括号”<>”提供明白无误的模板参数(也就是提供容器中的元素类型),所以加入以下代码:
- int main()
- {
- myvector <int> tmpvector1; //T被替换成int
- myvector <double> tmpvector2;//T被替换成double
- myvector
tmpvector3;//T被替换成string -
- return 0;
- }
main函数中的 myvector
注意:myvector 是类模板名,不是一个类型名(或者说是一个残缺的类型名),类模板是用来实例化类型的。所以myvector <int>、 myvector <double>和 myvector <string>才是真正的类型名。所以可以看出,一个通过类模板实例化了的类类型总会用尖括号包含着模板参数。
类模板成员函数可以写在myvector.h的vector类模板声明中,这种写在类模板声明中的成员函数就被隐式声明为内联函数。
当然,类模板的成员函数声明也可以写在vector类模板声明中,而在vector类模板声明之外实现。
下面在myvector.h的myvector类模板中,增加如下成员函数的声明:
- public:
- myfunc();
在myvector.cpp文件中这样定义成员函数:
- template<typename T>
- void myvector
::myfunc()//实例化构造函数 - {
- cout << "类模板成员函数!" << endl;
- }
有一点值得注意, 一个类模板虽然里面可能有很多成员函数,但是,当实例化模板之后, 如果后续没有使用到某个成员函数,则这个成员函数是不会被实例化的。换句话说,一个实例化的模板,它的成员只有在使用的时候才会被实例化〈程序员编写的代码中出现了调用该成员函数的代码) 。
在类模板myvector中有一个赋值运算符的重载代码:
myvector& operator=(const myvector&);
上面的赋假运算符重载返回一个myveclor 的引用。请注意,在类模板内部,可以直接使用类模板名,并不需要在类模板名后跟模板参数.因为在类模板定义内部,如果没提供类模板参数,编译器会假定类模板名带与不带模板参数等价(也就是 myvector 等价于myvector < T >).
myvector& operator=(const myvector&);//等价:myvector& operator=(const myvector&);
但是需要注意的是:
如果在类模板定义之外实现这个赋值运算符重载,需要这样写:
- template<typename T>
- myvector
& myvector::operator=(const myvector&)//第一个表示返回的是一个实例化了的myvector,第三个不是必加 - {
- return *this;
- }
模板参数并不局限于类型,普通的值也能作为模板参数,也就是非类型模板参数。
myvector类模板中是一个类型模板参数,那么看看非类型模板参数怎么用,下面创建一个类模板来演示。
在.h中声明一个类模板带非类型模板参数,如下:
- template<typename T,int size = 10>
- class myarray
- {
- private:
- T arr[size];
- };
在.cpp的main函数中,增加如下代码:
myarray<int,100> tmparr;
当然,也可以使用默认的非类型模板参数值,这样就可以少传递一个模板参数:
myarray<int> tmparr; //size默认值为10
同时也要注意,myarray类模板里面有两个模板参数,在类定义之外书写成员函数实现的时候也要注意写法。首先,在类模板内增加一个用public修饰的myfunc成员函数的声明。代码如下:
- public:
- void myfunc();
在.cpp文件中定义myarray成员函数的实现:
- template<typename T, int size>
- void myarray
::myfunc() - {
- cout << "size" << endl;
- }
在main函数中,加入如下代码来做一下调用:
- int main()
- {
- myarray<int>tmparr1;
- tmparr1.myfunc(); //10
-
- myarray<int,100>tmparr2;
- tmparr2.myfunc(); //100
-
- return 0;
- }
注意:这种非类型的模版参数,参数的类型还是有一定限制的,如下定义会报错:
(1)浮点型一般不能作为非类型模板参数。
- template<typename T,double size>
- class myarray
- {
- ......
- }
(2)类类型也不难作为非类型模板参数。
- class A{
- ......
- };
-
- template<typename T,A size>
- class myarray
- {
- ......
- };
2022.08.11结。