模版的分离编译 解决方案
回顾
对于模版,在之前我们就已经讲过,模版不支持分离编译(即声明定义不在同一文件中)。
类中,我们知道,对于代码量比较小的函数,会默认识别成内联函数,增加代码运行的效率,而一些代码量比较大的函数,则仍然进行函数调用。
但是有些函数实在长,如果这些函数全写在类里面,十分不利于我们对代码的可读性,所以大佬就有这么两个办法来完善这些问题。
原因分析
首先我们要明白为什么模版对于声明定义分离会报错?
实际上,报的是链接错误
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;
}
模版的分离编译 解决方案相关推荐
- 如何解决类模板的分离编译问题?
一模板: 模板不是数据类型,只能算是一种行为集合的表示.编译器在使用模板时,通过更换模板参数来创建数据类型.这个过程就是模板实例化(Instantiation), 从模板类创建得到的类型称之为特例(s ...
- C++ 泛型编程(二):非类型模板参数,模板特化,模板的分离编译
目录 非类型模板参数 函数模板的特化 类模板的特化 全特化 偏特化 部分参数特化 参数修饰特化 模板分离编译 问题分析 解决方法 非类型模板参数 模板的参数分为两种,一种是非类型参数,一种是类型参数. ...
- C++模板——模板特化、分离编译
这里写目录标题 一.泛型编程与模板 二.函数模板 1.概念 2.原理 3.函数模板的实例化 3. 模板实现复数类的加法 4.模板参数的匹配原则 三.类模板 1.定义格式 用类模板实现顺序表 2.非类型 ...
- c++模板函数声明定义分离编译错误详解
今天看到accelerated c++上有个简单的vector容器的实现Vec,就再vs2008上编译了下: / Vec.h #ifndef GUARD_VEC_H #define GUARD_VE ...
- Eclipse不编译解决方案
Eclipse不编译解决方案 参考文章: (1)Eclipse不编译解决方案 (2)https://www.cnblogs.com/yzw23333/p/7560045.html 备忘一下.
- PaddleOCR——Visual Studio 2019 环境下C++推理部署 CMake 编译解决方案
PaddleOCR--Visual Studio 2019 环境下C++推理部署 CMake 编译解决方案 官方文档 环境配置 Step1: 下载PaddlePaddle C++ 预测库 paddle ...
- C++ 函数模板与分离编译模式
代码编译运行环境:VS2017+Debug+Win32 1.分离编译模式 一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件连接起来形成单一的可执行文件的过程 ...
- R+markdown+LaTeX 中文编译解决方案
R+markdown+LaTeX 中文编译解决方案 参考文章: (1)R+markdown+LaTeX 中文编译解决方案 (2)https://www.cnblogs.com/loca/p/45416 ...
- 使用Cmake生成跨平台项目编译解决方案
项目最近有需求在windows下面运行,我花了几周时间将linux的服务器移植到windows下面,目前已经能够正常运行服务器,目前又有了新需求,两边的代码结构和组织是分开的,因此为了两边能够同步维护 ...
最新文章
- mvvm command的使用案例
- CMU贺斌教授团队提出FAST-IRES技术,利用高密度EEG产生脑信号源的动态图像
- A folder failed to be moved——Android SDK的安装问题解决方案
- 四十四、Python中的statsmodels模块
- wxWidgets:工具栏概述
- [号外] Blazor wasm 其实也挺快!
- 想通过好的商业模式赚钱,应该钻研“道”还是“术”呢?
- Revit Family API 添加几何实体
- 第三天.SQL语言基础
- 全新版本的SoundSource for Mac - 优质的音频控制软件
- VS2010+OpenCV2.4.6永久性配置方法
- hough变换检测圆周_Hough变换检测圆(附:MATLAB程序)
- UI设计中有哪些常见的风格?
- java ready()_Java BufferedReader ready()用法及代码示例
- 小伙伴们,线程生命周期、线程池生命周期别再傻傻分不清楚了!!!
- APP 跳转微信小程序和回调
- 显示器支架什么品牌好?
- 【Python量化交易笔记】股票数据获取 (一)
- 工作中常用的oracle数据库sql
- Linux 安装 Consul并设置开机启动