无论是普通类,还是类模板,它的成员函数本身可以是一个函数模板,这种成员函数称为“成员函数模板”,但是这种成员函数模板不可以是虚函数,如果写一个虚函数模板,编译器就会报错。
在.cpp文件中,定义一个类A如下:
- class Test{
- public:
- template<typename T>
- void mytest(T tmpt)
- {
- cout<
- }
- };
在main函数中这样调用:
- Test t;
- t.mytest(20);//打印出20
二、类模板的成员函数模板
类模板,也是可以为它定义成员函数模板的,这种情况就是类模板和其它成员函数模板都有各自独立的模板参数。
把上面的类Test改一下,改成一个类模板。注意这里构造函数也引入自己的模板参数,该模板参数与整个类的模板参数没有任何关系:
- template<typename T1>
- class Test
- {
- public:
- template<typename T2>
- Test(T2 num1,T2 num2)
- {
- cout <<"num1=" << num1 << ",num2=" << num2 << endl;
- }
- public:
- template<typename T3>
- void mytest(T3 tmpt)
- {
- cout <<"tmpt=" << tmpt << endl;
- }
- T1 testNum;
- };
在main函数如下所示:
- Test<float>test1(1,2);
- test1.mytest(55);
- test1.testNum = 100.123;
- cout << test1.testNum << endl;
-
- cout << endl;
- Test<int>test2(1.234, 2.678);
- test2.mytest(3.1415926);
- test2.testNum = 200;
- cout << test2.testNum << endl;
从上面例子可以看到,类模板本身有自己的模板参数T1,而成员函数模板Test和mytest也有自己的模板参数,T2和T3两者之间互不干扰。
现在如果把上面Test类模板的声明和定义分开写,怎么来写呢?
在.h文件中写Test类模模板的声明:
- template<typename T1>
- class Test
- {
- public:
- template<typename T2>
- Test(T2 num1, T2 num2);
- public:
- template<typename T3>
- void mytest(T3 tmpt);
- T1 testNum;
- };
在.cpp文件中定义类中的构造函数和普通函数如下:
- template<typename T1>
- template<typename T2>
- Test
::Test(T2 num1,T2 num2) - {
- cout <<"num1=" << num1 << ",num2=" << num2 << endl;
- }
-
- template<typename T1>
- template<typename T3>
- void Test
::mytest(T3 tmpt) - {
- cout <<"tmpt=" << tmpt << endl;
- }
总结:
(1)类模板中的成员函数,只有源程序代码中出现调用这些成员函数的代码时,这些成员函数才会出现在一个实例化了的类模板中。
(2)类模板中的成员函数模板,只有源程序代码中出现调用这些成员函数模板的代码时,这些成员函数模板的具体实例化才会出现在一个实例化了的类模板中。
三、模板显示实例化与声明
模板只有被使用的时候才会被实例化。
我们在test00.h文件中声明:
- #ifndef __TEST_H__
- #define __TEST_H__
-
- #include
- #include
-
- template<typename T1>
- class Test00
- {
- public:
- template<typename T2>
- Test00(T2 num1, T2 num2);
- public:
- template<typename T3>
- void mytest(T3 tmpt);
- T1 testNum;
- };
-
- #endif // !__Test00_H__
在test00.cpp文件中这样定义:
- #include"test00.h"
- using namespace std;
-
- template<typename T1>
- template<typename T2>
- Test00
::Test00(T2 num1,T2 num2) - {
- cout <<"num1=" << num1 << ",num2=" << num2 << endl;
- }
-
- template<typename T1>
- template<typename T3>
- void Test00
::mytest(T3 tmpt) - {
- cout <<"tmpt=" << tmpt << endl;
- }
我们在项目工程中添加一个test01.h和一个test01.cpp文件,然后做以下两件事,
(1)然后在test01.h中这样写:
- #ifndef __TEST01_H__
- #define __TEST01_H__
-
- #include "test00.h"
-
- void myTest01();
-
- #endif
(2)在test01.cpp文件中这样写:
- #include"test00.h"
-
- using namespace std;
-
- void myTest01()
- {
- Test00 <float>fTest00(1, 2);
- }
在main主函数中这样写:
- int main()
- {
- Test00<float>fTest00(1,2);
- fTest00.mytest(55);
- fTest00.testNum = 100.123;
- cout << fTest00.testNum << endl;
-
- myTest01();
-
- return 0;
- }
现在编译这个项目,我们已经知道:这些.cpp源文件对于编译器来讲都是独立编译的(每个.cpp编译后可能生成一个.obj文件,多个.cpp编译后自然生成多个.obj文件)。
当这两个.cpp代码中的“Test00 <float>fTest00(1, 2);”这行代码在编译时,因为每一个.cpp文件独立编译,所以编译器在test00.cpp中会实例化一个Test00类(也可以叫模板类Test00的一个实例),在test01.cpp中也会实例化一个Test00类,如果在多个.cpp中实例化类模板Test00,那这个项目的开销会比较大,增加很多编译时间并且没有必要,这里我们可以通过“显式实例化”来解决这种生成多个相同类模板实例的开销。
在test00.cpp文件头(也就是定义Test00类构造函数模板和成员函数模板的文件中)写入:
template Test00<float>;
这段代码表示的意思就是让编译器实例化出一个Test00。在其它的.cpp文件中就不需要再实例化,只需要在其它的.cpp文件头声明这个实例化出来的类就行( 或在Test00.h头文件中),这样写:
extern template Test00<float>;
这个带extern的代码行被称为模板实例化声明。当编译器遇到extern模板实例化声明时,就不会在本.cpp源文件中生成一个extern后面所表示的类模板的实例化版本代码,意思就是告诉编译器,在其它的.cpp源文件中已经有一个该类模板的实例化版本了。
需要记住:模板实例化定义的格式是以template开头,而模板实例化声明的格式是以extern template开头。函数模板也一样。
在test00.h头文件中这样声明:
- template<typename T>
- void myfunc(T tNum1,T tNum2);
在test00.cpp文件中这样定义:
- template<typename T>
- void myfunc(T tNum1,T tNum2)
- {
- cout<
- }
假如在main主函数中这样调用:
myfunc(40,50);
在test01.cpp的myTest01函数中这样调用myfunc函数模板:
- void myTest01()
- {
- Test00 <float>fTest00(1, 2);
- myfunc(100,200);
- }
那么在test00.cpp文件头这样写:
template void myfunc(int &tNum1,int &tNum2);
在test01.cpp文件头这样写(或者在test00.h头文件中写):
extern template void myfunc(int &tNum1,int &tNum2);
注意:模板的实例化定义只有一个,模板的实例化声明可以有多个。实例化定义不要忘记写,否则就达不到减少系统额外开销的效果或者造成链接出错。
2022.08.11结。
-
相关阅读:
零基础数据科学学习 Python 的 4 个阶段
【落地应用】华为之图神经网络在推荐系统中的应用
数电学习(六、时序逻辑电路)(二)
【云原生之Docker实战】使用Docker部署draw.io思维导图工具
RabbitMQ 入门系列:7、保障消息不重复消费:产生消息的唯一ID。
自动化测试和性能测试的区别
软件工程 第一次随堂练习
PDF水印怎么加?分享三个添加水印小妙招
计算机毕业设计springboot+vue景区疫情预警系统
基于量子粒子群算法(QPSO)优化LSTM的风电、负荷等时间序列预测算法(Matlab代码实现)
-
原文地址:https://blog.csdn.net/euxnijuoh/article/details/126291119