您的位置:首页 > 编程语言 > C语言/C++

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

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

假如我们要编写一个判断两个变量是否相等的函数,那么为了处理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)
4000
{
_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类型的顺序表:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: