一直以为模板类是特别神奇的东西,它能够构造出不一样类型的对象,使代码更加的灵活。这个过程就是类模板的实例化。ide
咱们使用类的模板写一个stack类:函数
#include<assert.h> #include"Seqlist1.h" using namespace std; template<class T,template<class> class Container = Seqlist> class Stack { public: void Push(const T& x) { _con.Pushback(x); } void Pop() { _con.Popback(); } bool Empty() { return Size() == 0; } size_t Size() { return _con.Size(); } T& Top() { size_t size = Size(); assert(size > 0); return _con[size - 1]; } void print() { _con.Print(); } T& operator[](size_t index) { return _con[index]; } protected: Container _con; };
其中咱们能够传入不一样类型的T,class Continer是类型T的一个容器,这里默认是用自定义的一个顺序表来当容器。编译器就会构造出不一样的代码出来。spa
这样的实现方式是在类内定义成员函数,并把整个类放在.h中,这种方式叫作类模板的包含模式。这样的定义方式是有好处的,要使用Stack这个类,只要在头文件内包含类体就能够了,编译器很容易就能够找到类的定义。固然这种方式是有弊端的,若是一个类的成员函数个数不少而且很复杂,无疑在你去阅读或者修改定义的时候会让人很头疼,不能快速找到成员函数的定义。对象
还好C++提供了另一种方式——将成员函数的声明放在类体内.h中,将函数的定义放在.cpp中,就至关与类外定义,咱们把这种方式叫作类模板的分离模式。这样写出来的代码是很美观的,而且易于修改。编译器
//.h文件 #include<assert.h> #include"Seqlist1.h" using namespace std; template<class T,template<class> class Container = Seqlist> class Stack { public: void Push(const T& x); void Pop(); bool Empty(); size_t Size(); T& Top(); void print(); T& operator[](size_t index); protected: Container _con; }; //.cpp文件 //类外定义 template<class T,template<class> class Container = Seqlist> void Push(const T& x) { _con.Pushback(x); } template<class T,template<class> class Container = Seqlist> void Pop() { _con.Popback(); } template<class T,template<class> class Container = Seqlist> bool Empty() { return Size() == 0; } template<class T,template<class> class Container = Seqlist> size_t Size() { return _con.Size(); } template<class T,template<class> class Container = Seqlist> T& Top() { size_t size = Size(); assert(size > 0); return _con[size - 1]; } template<class T,template<class> class Container = Seqlist> void print() { _con.Print(); } template<class T,template<class> class Container = Seqlist> T& operator[](size_t index) { return _con[index]; }
类模板的分离模式就会牵扯出一些问题,好比说编译器在构造对象的时候它怎么找函数的定义的,由于函数的定义是在.cpp文件中的。这就是类模板的分离编译问题。it
假设咱们stack类是放在stack.h中的,成员函数的定义放在stack.cpp中的,而后类的调用放在main.cpp中,而且在main.cpp中包含了stack.h。编译
按照咱们一般的思路:
模板
编译main.cpp时,编译器不知道f(f是成员函数中的任意一个函数)的实现,因此当碰到对它的调用时只是给出一个指示,指示链接器应该为它寻找f的实现体。这也就是说main.obj中没有关于f的任何一行二进制代码。class
编译test.cpp时,编译器找到了f的实现。因而乎f的实现(二进制代码)出如今test.obj里。 test
链接时,链接器在test.obj中找到f的实现代码(二进制)的地址(经过符号导出表)。而后将main.obj中悬而未决的call XXX地址改为f实际的地址,pofect。
可是当你编译的时候,编译器就会报连接错误,找不到成员函数的定义。说明咱们的思路是错误的。对于模板,在没有实例化出对象以前是不会被编译成二进制代码的,而实例化是在程序运行的时候(也就是用到模板的时候)才实例化出对象,实例化是在编译器对代码进行编译的后面才发生的。这样问题就迎刃而解了,咱们得手动的在main中包含.cpp文件,让编译器知道有函数的定义,这样编译器就不会报错了。
这里有必要提醒(总结)一下的是:
若是你的类不是用模板实现的,就不会有分离编译的问题,也就是说,即便你的类的成员函数是在类外定义的也不用include .cpp文件,无论你用没用到都会实例化出代码出来,就会编译成二进制文件,在连接时就会连接起来。
若是你的类是模板类,而且想要在类外定义成员函数,就必须include .cpp文件。