一、“经典模型”的失效
我们学过C++的人都知道,在C++中组织代码的经典模型是:将函数或类的声明和定义部分分开在不同的文件之中 ,
即一般将声明放在一个.h的头文件中而定义在放在一个.cpp文件之中,当然这的确是写代码的一种很优良的风格,但问题
是如果将这种“经典模型”应用到模版上时就会发生连接上错误。
例如:
文件“A.h”
#include"iostream" using namespace std; #pragma once template<typename T> class A { private: T t; public: A(T d) {t = d;} ~A(void){} void fun(); };
文件“A.cpp”
#include "A.h" #include"iostream" using namespace std; template<typename T> void A<T>::fun() { cout<<t<<"这个是A类"<<endl; }
主文件main.cpp:
#include<iostream> #include"A.h" using namespace std; int main() { A<int> a(1); a.fun(); }
将上面的程序编译连接运行,会发现编译通过,但连接会报错。
上述这种“经典模型”不再适用与C++模版的主要原因是:模版类或模版函数与一般的C++类或函数不同,对于模版
类或模版函数只有在遇到不同类型的应用时才会实例化出具体不同的函数或类,所以上面的程序连接错误的主要原因是
:模版类A未被实例化,编译器会分别的去编译那三个文件,因为A的定义和A的调用分别处在不同的.cpp文件中,所以当
A.cpp被编译时因未见到具体类型调用所以没实例化;而main.cpp在编译时编译器只从A.h看到了A类的声明,所以编译
器会假设程序在别的地方提供了A的具体定义,所以也没有实例化A。以至于在连接过程中发现A<int>没有被实例化的定
义而最终报错。
二、“包含模型”的出现
“包含模型”解决了上述问题,即将模版类的定义和声明都写在同一个.h文件即可。这样只要主文件包含了这个.h头文件
则在主文件编译时就会从该.h文件中找到模版类或函数的定义从而产生实例化。
例如:文件“A.h”
#include"iostream" using namespace std; #pragma once template<typename T> class A { private: T t; public: A(T d) {t = d;} ~A(void){} void fun() { cout<<t<<"这个是A类"<<endl; } };
主文件“main.cpp”
#include<iostream> #include"A.h" using namespace std; int main() { A<int> a(1); a.fun(); }
可以运行通过。
三、显示实例化
显示实例化的出现主要是为了模版代码的组织形式的更加灵活性,同时也是对于“包含模型”的某些缺点的弥补,
在“包含模型”中有一个很大的不足就是:将所有的代码都写入一个.h头文件中这会明显的增加头文件的开销,且同时
对于应用该模版的每一个.cpp文件来说都要把此.h文件包含进去,显然这样做会更进一步的使整个程序的开销变的很
大,所以解决这个问题的一个办法就是“显示实例化”。
在最上面的那个例子中连接报错的原因是A<int>未被实例化,找不到具体的定义。所以如果我们还是想沿用经得
起历史考验的“经典模型”时,就需要自己去显示实例化。
“显示实例化”的具体形式很简单:只要在模版定义的.cpp文件中加上一句有显示实例化指示符template引导的需要
实例化实体的声明即可。
例如:对于最上面的例子:再要在A.cpp中在上下面的声明即可:
template A<int>;
例如完整例子修改后如下:
文件“A.h”
#include"iostream" using namespace std; #pragma once template<typename T> class A { private: T t; public: A(T d) {t = d;} ~A(void){} void fun(); };
文件“A.cpp”
#include "A.h" #include"iostream" using namespace std; template A<int>;//显示实例化 template<typename T> void A<T>::fun() { cout<<t<<"这个是A类"<<endl; }
主文件“mian.cpp”
#include<iostream> #include"A.h" using namespace std; int main() { A<int> a(1); a.fun(); }运行通过!
四、“包含模型”与“显示实例化”的结合
其实“显示实例化”也有其自己的弊端,那就是对于模版类或模版函数的某一个具体类型的显示实例化必须唯一,不能重复
实例化,这也就是说在程序中你必须自己去跟踪模版的每一个实例化,否则就会出错。这样的问题在达到一定规模的大程序中
是相当困难的,所以将“包含模型”与“显示实例化”结合就是取各自之长,可以根据具体问题的要求而随意选择不同的模型。
具体的做法是:任然将模版的定义和声明都分开写在两个不同的文件,但这两个文件都必须是.h的头文件,且定义文件要
用#include包含声明文件。所以当自己希望使用“包含模型”时,只需要把定义文件.h包含在应用.cpp文件即可。而当自己需要使用
”显示实例化“时,只需要将声明.h文件包含在应用文件之中,并且同时在模版定义.h文件之中显示实例化所需要的具体实例即可。