回顾
对于模版,在之前我们就已经讲过,模版不支持分离编译(即声明定义不在同一文件中)。
类中,我们知道,对于代码量比较小的函数,会默认识别成内联函数,增加代码运行的效率,而一些代码量比较大的函数,则仍然进行函数调用。
但是有些函数实在长,如果这些函数全写在类里面,十分不利于我们对代码的可读性,所以大佬就有这么两个办法来完善这些问题。
原因分析
首先我们要明白为什么模版对于声明定义分离会报错?
实际上,报的是链接错误
ps:这里我们已经声明定义分离了
test.obj : error LNK2019: 无法解析的外部符号 "public: void __cdecl fjz::vector<int>::push_back(int const &)" (?push_back@?$vector@H@fjz@@QEAAXAEBH@Z),函数 main 中引用了该符号
这种报错一般是编译器没有找到对应的函数定义。
可是为什么?
这是因为在类外定义的函数无法实例化!!
模版类(函数)你可以理解为只是一个 模具 ,这个模具要根据你的模版参数T...进行实例化,而类外定义的函数因为不在类里面,没有办法跟着类的实例化一起实例化!
解决方案一
以vector举例
vector.h
template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; void reserve(size_t n); void push_back(const T& val); iterator erase(iterator pos); iterator erase(iterator first, iterator last); public: iterator _start; iterator _finish; iterator _end_of_storage; };
这里我省略掉了很多不需要声明定义分离的短函数。
vetcor.cpp
#include"vector.h" template<class T> void vector<T>::reserve(size_t n) { if (capacity() < n) //判断是否需要扩容 { size_t sz = size(); //保存size的数据 T* tmp = new T[n]; if (_start) //如果_start不为空指针,则进行拷贝和delete { for (size_t i = 0; i < sz; i++) { tmp[i] = _start[i]; //调用赋值来完成深拷贝 } delete[] _start; } _start = tmp; _finish = _start + sz; _end_of_storage = _start + n; } } template<class T> void vector<T>::push_back(const T& val) { if (_finish == _end_of_storage) { reserve(empty() ? 4 : capacity() * 2); //如果数据为空,则给初始空间为4 } *_finish = val; _finish++; } template<class T> typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator pos) { assert(pos < _finish); for (int i = 0; i < _finish - pos - 1; i++) { pos[i] = pos[i + 1]; } --_finish; return pos; } template<class T> typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator first, typename vector<T>::iterator last) { assert(last <= _finish); assert(first <= _finish); size_t len = last - first; for (int i = 0; i < _finish - last; i++) { first[i] = last[i]; } _finish -= len; return first; }
仔细看分离定义的格式,对于iterator 这是我们在类中重定义的类型,必须要声明他是一个在vector中的类型,否则无法编译!
目前这样就已经是声明定义分离的标准格式了,如果我们现在进行vector相关的函数调用,就会出现我们刚刚上面的说的链接问题, .cpp中定义的函数无法实例化
那么解决办法是什么?
让它实例化!
#include"vector.h"
template<class T>
void vector<T>::reserve(size_t n)
{
if (capacity() < n) //判断是否需要扩容
{
size_t sz = size(); //保存size的数据
T* tmp = new T[n];
if (_start) //如果_start不为空指针,则进行拷贝和delete
{
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i]; //调用赋值来完成深拷贝
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
template<class T>
void vector<T>::push_back(const T& val)
{
if (_finish == _end_of_storage)
{
reserve(empty() ? 4 : capacity() * 2); //如果数据为空,则给初始空间为4
}
*_finish = val;
_finish++;
}
template<class T>
typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator pos)
{
assert(pos < _finish);
for (int i = 0; i < _finish - pos - 1; i++)
{
pos[i] = pos[i + 1];
}
--_finish;
return pos;
}
template<class T>
typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator first,
typename vector<T>::iterator last)
{
assert(last <= _finish);
assert(first <= _finish);
size_t len = last - first;
for (int i = 0; i < _finish - last; i++)
{
first[i] = last[i];
}
_finish -= len;
return first;
}
template
vector<int>; //实例化vector<int>
template
vector<double>; //实例化vector<double>
这样就能实例化了,可是这种办法缺陷也很明显,你需要在.cpp文件中提前加入需要实例化的类型,没有加入的则仍然会编译错误,所以这种办法我们很少用,干脆不如就不要不同文件分离编译。
而下面介绍的方案二这种办法也是比较推崇的方法
解决方案二
在同一文件进行声明定义分离,这也是最好的办法,STL也是采用的这样的办法。
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
void reserve(size_t n);
void push_back(const T& val);
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);
public:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
template<class T>
void vector<T>::reserve(size_t n)
{
if (capacity() < n) //判断是否需要扩容
{
size_t sz = size(); //保存size的数据
T* tmp = new T[n];
if (_start) //如果_start不为空指针,则进行拷贝和delete
{
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i]; //调用赋值来完成深拷贝
}
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
template<class T>
void vector<T>::push_back(const T& val)
{
if (_finish == _end_of_storage)
{
reserve(empty() ? 4 : capacity() * 2); //如果数据为空,则给初始空间为4
}
*_finish = val;
_finish++;
}
template<class T>
typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator pos)
{
assert(pos < _finish);
for (int i = 0; i < _finish - pos - 1; i++)
{
pos[i] = pos[i + 1];
}
--_finish;
return pos;
}
template<class T>
typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator first, typename vector<T>::iterator last)
{
assert(last <= _finish);
assert(first <= _finish);
size_t len = last - first;
for (int i = 0; i < _finish - last; i++)
{
first[i] = last[i];
}
_finish -= len;
return first;
}