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

c++:模板的类型萃取

2017-11-19 20:07 543 查看
首先,需要举一个例子来说明c++在什么情况下需要对不同的类型不同对待。

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

void CheckCapacity()
{
if (_size >= _capacity)
{
_capacity = _capacity > 0 ? _capacity * 2 : 3;//当容量为0时增容为3,之后每次增2倍
T* newA = new T[_capacity];
if (_a != NULL)
{
memcpy(newA, _a, _size*sizeof(T));//使用memcpy
}
delete[] _a;
_a = newA;

}
}

void PushBack(const T& x)
{
CheckCapacity();
_a[_size++] = x;
}

void Print()
{
for (size_t i = 0; i < _size; i++)
{
cout << _a[i] << " ";
}
cout << endl;
}

private:
T* _a;
size_t _size;
size_t _capacity;
};


这是一个模板类的顺序表,实现了尾插和增容两个接口。

接下来进行测试:

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


它是没有问题的:



但是如果里面存string类型:



可以看到,它崩溃了,而且打印出来了数据,说明是在析构的时候崩溃,到底怎么回事?



如图,因为使用得是memcpy,所以新开辟的空间的前三个_str和_a指向的空间的每一个_str指向的一样。

这样就导致delete[] _a的时候,新开辟的空间的前三个_str为野指针,必然崩溃。

如果需要解决问题,只要这样做:

for (size_t i = 0; i < _size; i++)
{
newA[i] = _a[i];
}




利用operator=,通过for循环依次赋值,这样新旧_str指向的空间不同,解决了问题。

现在就出现了需要对不同的类型不同对待的情况,因为memcpy的效率比for循环高得多,但是对特殊类型又不得不使用for循环。

使用全特化可以解决:

template <>
class SeqList<int>
{
public:
SeqList()
:_a(NULL)
, _size(0)
, _capacity(0)
{}

void CheckCapacity()
{
if (_size >= _capacity)
{
_capacity = _capacity > 0 ? _capacity * 2 : 3;
int* newA = new int[_capacity];
if (_a)
{
memcpy(newA, _a, _size*sizeof(int));
}
delete[] _a;
_a = newA;
}
}

void PushBack(const int& x)
{
CheckCapacity();
_a[_size++] = x;
}

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

private:
int* _a;
size_t _size;
size_t _capacity;
};


template <>
class SeqList<string>
{
public:
SeqList()
:_a(NULL)
, _size(0)
, _capacity(0)
{}

void CheckCapacity()
{
if (_size >= _capacity)
{
_capacity = _capacity > 0 ? _capacity * 2 : 3;
string* newA = new string[_capacity];
if (_a)
{
for (size_t i = 0; i < _size; i++)
{
newA[i] = _a[i];
}
delete[] _a;
}
_a = newA;
}
}

void PushBack(const string& x)
{
CheckCapacity();
_a[_size++] = x;
}

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

private:
string* _a;
size_t _size;
size_t _capacity;
};


这不是一个好办法,这样做会让代码的复用性很差,全是重复代码。

现在就该类型萃取出场了:

struct __TrueType//一个空类
{};

struct __FalseType//空类
{};

template <class T>
struct __TypeTraits
{
typedef __FalseType ISPODType; //默认为不是基本类型 POD(基本类型)
};

template <>
struct __TypeTraits<int>
{
typedef __TrueType ISPODType; //int是基本类型
};

template <class T>
T* TypeCopy(T* dst, const T* src, size_t n)
{
return __TypeCopy(dst, src, n, __TypeTraits<T>::ISPODType());
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n,__TrueType)
{
cout << "memcpy()" << endl;
return (T*)memcpy(dst, src, n*sizeof(T));
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n, __FalseType)
{
for (size_t i = 0; i < n; i++)
{
dst[i] = src[i];
}
cout << "operator=()" << endl;
return dst;
}


想要读懂这段代码不容易,我总结一下:





通过TypeCopy,那么就可以根据类型来确定拷贝方案。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 类型萃取