问题引入
我们创建三个文件,Stack.h Stack.cpp Test.cpp
//Stack.h中存放函数声明
#pragma once
#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
void func();
//Stack.cpp中存放对函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
//错解
template<class T>
T Add(const T& left, const T& right) {
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
void func() {
cout << "void func()" << endl;
}
//Test.cpp中存放主函数
#include"Stack.h"
#include<iostream>
using namespace std;
int main() {
Add(1, 2);
func();
return 0;
}
如果我们编译运行,hui会发现编辑错误,产生无法解析的外部符号。要理解为什么会出错,就需要了解一下编辑器的编译过程。
一、编译器编译过程
Stack.h Stack.cpp Test.cpp
1、预处理——头文件的展开/宏替换/…
生成文件:Stack.i Test.i
2、编译——检查语法生成汇编代码
函数定义里,会对func生成汇编代码,但不会对Add()生成汇编指令
3、汇编——汇编代码转换二进制机器码
Stack.o Test.o
4、链接
q.out
Add找不到,而func可以找到。Stack.cpp因为Add没有实例化,没有Add的地址,而func可以找到
二、正解
2.1、显示实例化
因为编辑器无法对Add进行实例化,那我们告诉他该怎么实例化就好了。
//正解
//显示实例化
template
int Add<int>(const int& left, const int& right);
添加以上代码到Stack.cpp里,会使编辑器能过正常运行。
函数模版的实例化
template<class T>
class Stack
{
public:
void Push(const T& x);
void Pop();
private:
T* _a=nullptr;
int _top=0;
int _capacity=0;
};
template<class T>
void Stack(<T>::Push(const T& x)
{
cout << "void Stack(<T>::Push(const T& x)" << endl;
}
template<class T>
void Pop()
{
cout << " void Pop()" << endl;
}
template<class T>
void Stack<T>::Pop()
{
cout << "void Pop()" << endl;
}
template
class Stack<int>;
#include"Stack.h"
#include<iostream>
using namespace std;
int main() {
/*Add(1, 2);
func();*/
Stack<int> st;
st.Push(1);
st.Pop();
return 0;
}
运行结果:
但这样我们每次想多用一个模版,就需要专门再实例化一个,岂不是使模版失去了其方便的意义。
可行,但不好用
2.2、将Stack.cpp里的函数实现放在Stack.h中实现,丢弃Stack.cpp。
//更新后的Stack.h,可以看到已经没有实例化了
#pragma once
#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
void func();
template<class T>
class Stack
{
public:
void Push(const T& x);
void Pop();
private:
T* _a=nullptr;
int _top=0;
int _capacity=0;
};
#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
template<class T>
T Add(const T& left, const T& right) {
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
void func() {
cout << "void func()" << endl;
}
template<class T>
void Stack<T>::Push(const T& x)
{
cout << "void Stack(<T>::Push(const T& x)" << endl;
}
template<class T>
void Pop()
{
cout << " void Pop()" << endl;
}
template<class T>
void Stack<T>::Pop()
{
cout << "void Pop()" << endl;
}
为什么声明和定义都放到.h文件后就没问题了?
因为在预处理时,展开头文件,这时头文件里不仅有声明还有定义,编辑器直接找到了类模板的定义,将类模板实例化,确定了地址。
三、模版总结
优点
1、模版复用了代码,节省资源,更快的迭代开发,C++的标准模版库(STL)因此而产生。
2、增强了代码的灵活性
缺点
1、模版会导致代码膨胀问题,也会导致编译时间变长。
2、出现模版编译错误时,错误信息非常凌乱,不易确定错误