从类模板引发的思考
C++中使用了模板来减少方法相同但是类型不一样带来的函数重载以及大量复制代码的问题。这里主要说说类模板
类模板的定义:
template<typename Type> class Stacks { public: Stacks(void); Stacks(int nSize); Stacks(Type Arr[],int nSize); ~Stacks(void); public: bool isEmpty(); bool isFull(); bool push(const Type &e); Type pop(); Type getTop(); int getLength(); void print(); private: int m_top; int m_nLength; Type *m_pArr; };
函数的实现:
函数实现既可以放在类定义体内,又可以放在类定义体外,但是不管放在体内还是体外都必须放在头文件中,这点和inline函数有点类似。
分离编译:
如果声明和定义分别放在h文件和cpp文件呢?
这个时候如果在其他程序中用到了模板类中的函数,那么就会出现
诸如此类的错误,错误原因说的很清楚,就是说没有找到相关的函数定义。那为什么模板类的非模板类在分离编译上这一点有区别呢?
我们知道分离编译是以编译单元为单位,所谓的编译单元就是cpp文件,编译器在进行编译的时候,会首先进行预编译(展开头文件),然后进行编译,将单独的编译单元生成2进制文件obj。最后编译器再利用link把用到的函数变量等链接起来生成可执行文件exe。这就是编译的过程。
但是这个过程在遇到模板类时候会发生一些变化,如果编译器能将模板类的实现函数编译成obj文件,那么在这些函数中遇到 Type(类型位置),编译器该怎么将代码翻译成对应的汇编代码呢,一些pull,push该对应多少字节呢,这些都是未知的,所以从理论上讲,即便我们将模板类的声明和实现相互分离,但是在生成的对应obj文件中不会有一个函数的2进制代码的。
类模板的特殊编译:
那么既然obj中没有二进制函数,main函数又是如何调用模板类中的函数呢,我们知道一般的类,main函数调用它只需要知道一个link地址就行,但是针对模板类就不一样了,它会在main函数所在的cpp里面把用到的函数实例化(就是用具体类型替换通用类型),这样就可以调用它了,因为要替换,所以就必须知道函数的定义了,这就是为什么必须得在一个文件中实现模板类的声明和定义。
测试和验证:
通过在模板类中增加一个这样的函数
template<typename Type> void Stacks<Type>::print() { cout<<"This is a test ,I love you" <<"I love you,I love you"<<endl; }
然后我们在main中调用print函数,在生成的debug文件中用notepad++打开main.obj,我们可以看到有这样的字符出现:
这就说明了问题,main.cpp中实例化了函数的定义。
附带栈结构实现的C++源代码
// Stacks.h
#pragma once #include <iostream> using namespace std; template<typename Type> class Stacks { public: Stacks(void); Stacks(int nSize); Stacks(Type Arr[],int nSize); ~Stacks(void); public: bool isEmpty(); bool isFull(); bool push(const Type &e); Type pop(); Type getTop(); int getLength(); void print(); private: int m_top; int m_nLength; Type *m_pArr; }; template<typename Type> Stacks<Type>::Stacks(void) { m_top = -1; m_nLength = 10; m_pArr = new Type[10]; if (m_pArr == NULL) cout<<"Allocate stack failed"<<endl; else m_top = m_nLength-1; } template<typename Type> Stacks<Type>::Stacks(int nSize) { m_top = -1; m_pArr = new Type[nSize]; if(m_pArr == NULL) cout<<"Allocate stack failed"<<endl; else { m_top = nSize-1; m_nLength = nSize; } } template<typename Type> Stacks<Type>::Stacks(Type Arr[],int nSize) { m_top = -1; m_pArr = new Type[nSize]; if(m_pArr == NULL) cout<<"Allocate stack failed"<<endl; else { for(int i=0;i<nSize;i++) m_pArr[i] = Arr[i]; m_top = nSize - 1; m_nLength = nSize; } } template<typename Type> bool Stacks<Type>::isEmpty() { return m_top == -1; } template<typename Type> bool Stacks<Type>::isFull() { return m_top == m_nLength - 1; } template<typename Type> Type Stacks<Type>::getTop() { if (m_top != -1) return m_pArr[m_top]; else { cout<<"The stack is empty"<<endl; exit(1); } } template<typename Type> Type Stacks<Type>::pop() { if(m_top == -1) { cout<<"The stack is empty"<<endl; exit(1); } else { m_top--; return m_pArr[m_top+1]; } } template<typename Type> bool Stacks<Type>::push(const Type &e) { if(m_top == m_nLength-1) { cout<<"The stack is full"<<endl; return false; } else { m_top++; m_pArr[m_top] = e; return true; } } template<typename Type> int Stacks<Type>::getLength() { return m_nLength; } template<typename Type> Stacks<Type>::~Stacks(void) { if(m_pArr != NULL) { delete m_pArr; m_pArr = NULL; } } template<typename Type> void Stacks<Type>::print() { cout<<"This is a test ,I love you" <<"I love you,I love you"<<endl; }//main.cpp
#include<iostream> #include<string> #include"Stacks.h" using namespace std; void main() { int a[7] = {1,2,3,4,5,6,7}; Stacks<int> Sta(a,7); cout<<Sta.isFull()<<endl; for(int i=0;i<7;i++) { int top = Sta.pop(); cout<<"The top is:"<<top<<endl; } cout<<Sta.isEmpty()<<endl; cout<<Sta.getLength()<<endl; Sta.print(); Sta.print(); system("pause"); }