在初识类型萃取之前,我们可以先写一个通用的Copy函数,当然选择使用模板
void Copy(T*dst, T*src, size_t size)//通用拷贝函数
{
memcpy(dst, src, sizeof(T)*size);
}
int main()
{
String s1[10]={"dddd", "sss", "ccc"};
String s2[10] = { "111", "222", "333" };
Copy(s2, s1, 10);
system("pause");
return 0;
}
通过监视窗口我们可以看到确实String s2[10]这个数组将s1的内容拷贝了过来
但是程序会崩溃,在这种情况下我们首先考虑是否存在内存泄露或者多次析构的问题
首先在进行拷贝的是Sring类的对象数组,而memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
此时s1,s2指向的是同一块内存,并且s2原有的内存丢失了,析构的时候先将s2析构掉,再将s1析构掉,此时同一块内存会被析构多次,则程序一定会崩溃。
此时我们不能选择用memcpy进行拷贝,选择使用for循环
void Copy(T*dst, T*src, size_t size)//通用拷贝函数
{
/*memcpy(dst, src, sizeof(T)*size);*/
for (int index = 0; index < size; index++)
{
dst[index] = src[index];
}
}
因为采用for循环是将对象数组的内容一个一个拷贝过来,而不是源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
比较mempy:由于是库函数,效率高,但涉及到内存问题极容易出错
for循环:不容易出错,但效率低。
如果我们拷贝的是int或者其它内置类型的数据使用memcpy不会涉及到内存问题,且效率高
如果我们涉及到自定义类型需要考虑内存问题最好使用for循环。
要注意上述程序中的’=‘是经过重载的
为了提高这个程序的质量,我们可以通过以下方法来实现
1、多传一个参数判断是深拷贝还是浅拷贝
void Copy(T* src, T* dst, size_t size,bool isPod=true)//通用拷贝函数
{
if (isPod)
{
for (size_t index = 0; index < size; ++index)
{
dst[index] = src[index];
}
}
else
memcpy(dst, src, sizeof(T)*size);
}
2、通过判断T的类型判断是深拷贝还是浅拷贝
struct _TrueType
{
static bool Get()
{
return true;
}
};
struct _FalseType
{
static bool Get()
{
return false;
}
};
template<class T>
struct TypeTraits
{
typedef _FalseType _IsPod;
};
template<>//类模板的特化
struct TypeTraits<int>
{
typedef _TrueType _IsPod;
};
template<>
struct TypeTraits<char>
{
typedef _TrueType _IsPod;
};
template<class T>
void Copy(T* src, T* dst, size_t size)//通用拷贝函数
{
if (TypeTraits<T>::_IsPod().Get())
{
memcpy(dst, src, sizeof(T)*size);
}
else
{
for (size_t index = 0; index < size; ++index)
{
dst[index] = src[index];
}
}
}
总结:在涉及内存方面一定要注意深浅拷贝的问题,采用类型萃取能够提高程序的质量。类型萃取我们是通过对函数模板的特化来实现的,尤其在涉及String类的时候一定要将‘=’运算符进行重载,我之前忘记将‘=’重载,在for循环里依然出了内存泄漏和多次析构的问题,写代码时还是得细心 。