C++模板类的类型萃取技术

模板是泛型编程的基础,所谓泛型编程就是指编写与类型无关的逻辑代码,是一种复用的方式。所以使用模板的目的就是方便程序员编写与类型无关的代码,减少相似代码在程序中出现的概率。

假如我们要编写一个判断两个变量是否相等的函数,那么为了处理int,char,double等不同类型的变量,我们得一一写出这些类型的重载函数。这样不仅代码会显得十分冗余,也会增加程序员无谓的工作量。为了解决这个问题,C++中引入了模板的概念。如下,我们可以利用模板来编写判断两个变量是否相等的函数。

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}

void Test1()
{
	string s1("xiaoxu");
	string s2("xiaoyu");
	cout << IsEqual(s1, s2) << endl;  //比较string类型的变量

	cout << IsEqual(11, 11) << endl;  //比较int类型的变量
	cout << IsEqual('1', '1') << endl;//比较char类型的变量
}

有了模板,我们不仅可以编写模板函数还可以编写模板类。以下就是利用模板实现顺序表的代码。

template<typename T>
class SeqList
{
public:
	SeqList()
		:_array(NULL)
		, _size(0)
		, _capacity(0)
	{}

	SeqList<T>(const SeqList<T>& s)
		:_array(new T[s._size])
		, _size(s._size)
		, _capacity(s._size)
	{
		memcpy(_array, s._array, sizeof(T)*s._size);
	}

	/*SeqList<T>& operator=(const SeqList<T>& s)
	{
		if (this != &s)
		{
			T* tmp = new T[s._size];
			memcpy(tmp, s._array, sizeof(T)*s._size);
			delete[] _array;
			_array = tmp;
			_size = s._size;
			_capacity = s._size;
		}

		return *this;
	}*/

	SeqList<T>& operator=(const SeqList<T>& s)
	{
		if (this != &s)
		{
			SeqList tmp(s);
			swap(_array, tmp._array);
			swap(_size, tmp._size);
			swap(_capacity, tmp._capacity);
		}

		return *this;
	}

	/*SeqList<T>& operator=(SeqList<T> s)
	{
		swap(_array, s._array);
		swap(_size, s._size);
		swap(_capacity, s._capacity);

		return *this;
	}*/

	~SeqList()
	{
		if (_array)
		{
			delete[] _array;
			_size = _capacity = 0;
		}
	}

	void PushBack(const T& d);
	void PopBack();
	void PushFront(const T& d);
	void PopFront();
	void Print();

	void Insert(size_t pos, const T& d);
	void Erase(size_t pos);
	size_t Find(const T& d);

	const T& Back();
	size_t Size();
	bool Empty();

protected:
	void CheckCapacity()
	{
		if (_size == _capacity)
		{
			_capacity = _capacity * 2 + 3;
			_a = (DataType*)realloc(_a, sizeof(DataType)*_capacity);
		}
	}
protected:
	T* _array;
	size_t _size;
	size_t _capacity;
};

template<typename T>
void SeqList<T>::PushBack(const T& d)
{
	_CheckCapacity();

	_array[_size] = d;
	_size++;
}

template<typename T>
void SeqList<T>::PopBack()
{
	if (_size > 0)
	{
		_size--;
	}
	else
	{
		cout << "顺序表为空,无法尾删" << endl;
	}
}

template<typename T>
void SeqList<T>::PushFront(const T& d)
{
	_CheckCapacity();

	for (size_t i = _size; i > 0; i--)
	{
		_array[i] = _array[i-1];
	}
	_size++;

	_array[0] = d;
}

template<typename T>
void SeqList<T>::PopFront()
{
	if (_size > 0)
	{
		for (size_t i = 0; i < _size; i++)
		{
			_array[i] = _array[i + 1];
		}
		_size--;
	}
	else
	{
		cout << "顺序表为空,无法头删" << endl;
	}
}

template<typename T>
void SeqList<T>::Insert(size_t pos, const T& d)
{
	_CheckCapacity();

	if (_size == 0)
	{
		PushFront(d);
	}
	else
	{
		for (size_t i = _size; i > pos - 1; i--)
		{
			_array[i] = _array[i - 1];
		}
		_array[pos - 1] = d;
		_size++;
	}
}

template<typename T>
void SeqList<T>::Erase(size_t pos)
{
	if (_size > 0)
	{
		for (size_t i = pos-1; i < _size; i++)
		{
			_array[i] = _array[i + 1];
		}
		_size--;
	}
	else
	{
		cout << "顺序表为空,无法删除数据" << endl;
	}
}

template<typename T>
size_t SeqList<T>::Find(const T& d)
{
	for (size_t i = 0; i < _size; i++)
	{
		if (_array[i] == d)
		{
			return i + 1;
		}
	}
	
	return 0;
}

template<typename T>
void SeqList<T>::Print()
{
	{
		if (_size > 0)
		{
			for (size_t i = 0; i < _size; i++)
			{
				cout << _array[i] << " ";
			}
			cout << endl;
		}
		else
		{
			cout << "顺序表为空" << endl;
		}
	}
}

由于模板不支持分离编译,所以我将类的成员函数的声明和定义写在了一个.hpp文件中。

以下为测试代码。

void Test1()
{
	SeqList<int> s1;
	s1.PushBack(1);
	s1.PushBack(2);
	s1.PushBack(3);
	s1.PushBack(4);
	s1.Print();

	//s1.PopBack();
	//s1.Print();
	//s1.PopBack();
	//s1.PopBack();
	//s1.PopBack();
	//s1.Print();
	//s1.PopBack();

	//s1.PushFront(4);
	//s1.PushFront(3);
	//s1.PushFront(2);
	//s1.PushFront(1);
	//s1.Print();

	//s1.PopFront();
	//s1.Print();
	//s1.PopFront();
	//s1.PopFront();
	//s1.PopFront();
	//s1.Print();
	//s1.PopFront();
}
输出结果:


void Test2()
{
	SeqList<string> s2;
	s2.PushBack("22");
	s2.PushBack("1111111111111111111111111111111111111111111111111111111111111111");
	s2.PushBack("33");
	s2.PushBack("44");
	s2.Print();
	//s2.PopBack();
	//s2.Print();
	//s2.PopBack();
	//s2.PopBack();
	//s2.PopBack(); 
	//s2.Print();
	//s2.PopBack();
}
输出结果:



可以看到,测试int类型时程序正常输出,测试string类型时程序却崩溃了。罪魁祸首是下面这条语句

memcpy(_array, s._array, sizeof(T)*s._size);

memcpy()是值拷贝函数,当我们使用它拷贝基本类型时没有任何问题,可是拷贝string类型时问题就出现了,VS下的string类型的实现原理是若字符串不长则以数组保存,若字符串过长(超过16),则通过指针在堆上开辟空间保存,所以当使用memcpy()函数拷贝较长的字符串(长度超过16)时,会直接拷贝指针,从而出现深浅拷贝的问题。

那么为了解决这个问题我们还得重新写一份代码吗?完全不用!用类型萃取技术即可解决。

类型萃取是一种常用的编程技巧,它可以使不同类型数据实现同一份函数代码。在调用时,事先并不知道对象是什么类型,编译器是开始编译后才进行模板推演,根据不同的对象类型生成相应的代码。以下就是用类型萃取优化上述代码。

struct __TrueType
{
	bool Get()
	{
		return true;
	}
};

struct __FalseType
{
	bool Get()
	{
		return false;
	}
};

template<typename T>
struct TypeTraits  //类型萃取
{
	typedef __FalseType IsPodType;  //Pod——内置类型
};

template<>
struct TypeTraits<int>  //特化内置类型
{
	typedef __TrueType IsPodType;
};
重新改写一下_Checkcapacity()函数即可,分成两种实现方式可提高代码运行效率。
void _CheckCapacity()
	{
		if (_size >= _capacity)
		{
			_capacity = _capacity * 2 + 3;

			if (TypeTraits<T>::IsPodType().Get())  //内置类型的实现方式
			{
				cout << "Pod Type" << endl;
				_array = (T*)realloc(_array, sizeof(T)*_capacity);
			}
			else   //非内置类型的实现方式
			{
				cout << "Not Pod Type" << endl;
				T* tmp = new T[_capacity];

				if (_array)
				{
					//memcpy(tmp, _array, sizeof(T)*_size);
					for (size_t i = 0; i < _size; i++)
					{
						tmp[i] = _array[i];
					}
					delete[] _array;
				}

				_array = tmp;
			}
		}
	}
成功输出string类型的顺序表:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值