模版的分离编译 解决方案

回顾

对于模版,在之前我们就已经讲过,模版不支持分离编译(即声明定义不在同一文件中)。

类中,我们知道,对于代码量比较小的函数,会默认识别成内联函数,增加代码运行的效率,而一些代码量比较大的函数,则仍然进行函数调用。 

但是有些函数实在长,如果这些函数全写在类里面,十分不利于我们对代码的可读性,所以大佬就有这么两个办法来完善这些问题。

原因分析

首先我们要明白为什么模版对于声明定义分离会报错?

实际上,报的是链接错误

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;
}

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风君子吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值