关于“非基本类型需慎用memcpy函数”的分析
假设我们要将一个元素类型为T的对象数组复制到另外一个数组中,我可以很轻松写出如下代码
T sour[N];
T dest[N];
int i;
for(i = 0; i < N; i++)
dest[i] = sour[i];
在C和C++中,上述代码都可以生效
然而,C程序员可能更喜欢用下面的方案
T sour[N];
T dest[N];
int i;
memcpy(dest, sour, N*sizeof(T));
这种做法在C中能很好的工作,然而如果T是非基本类型,上述代码在C++中就有可能会带来灾难,因为memcpy是按位拷贝,这涉及到深拷贝和浅拷贝的问题
如果类型T具有下面的特征,则不能对其使用memcpy
1.类型T需要显式定义复制构造函数和赋值操作符
2.类型T不需要显式定义复制构造函数和赋值操作符,但其底层数据成员需要显式定义复制构造函数和赋值操作符
参照下列代码
#include <iostream>
#include <string>
using namespace std;
//不能对B使用memcpy函数的原因是显而易见的
class B
{
public:
int *a;
B(int m = 0)
{
cout << "B的构造函数" << endl;
a = new int;
*a = m;
}
B(const B& b)
{
cout << "B的复制构造函数" << endl;
a = new int;
*a = *(b.a);
}
B& operator=(const B& b)
{
cout << "调用B的赋值操作符" << endl;
if(&b != this)
{
delete a;
a = new int;
*a = *(b.a);
}
return *this;
}
~B()
{
cout << "调用B的析构函数" << endl;
delete a;
}
};
//A不需要定义复制构造函数、赋值操作符、析构操作符,但也不能对A使用mencpy函数
class A
{
public:
B b;
A(int m = 0) : b(m) {}
};
void main()
{
A a1(1);
A a2(2);
cout << "测试A是否需要赋值操作符" << endl;
cout << "a1: " << a1.b.a << " " << *(a1.b.a) << endl;
cout << "a2: " << a2.b.a << " " << *(a2.b.a) << endl;
a1 = a2;
//memcpy(&a1,&a2,sizeof(A));//注意万万不可对A调用memcpy这种按位拷贝函数
cout << "a1: " << a1.b.a << " " << *(a1.b.a) << endl;
cout << "a2: " << a2.b.a << " " << *(a2.b.a) << endl;
cout << "测试A是否复制构造函数" << endl;
A a3(1);
cout << "a3: " << a3.b.a << " " << *(a3.b.a) << endl;
A a4(a3);
cout << "a4: " << a4.b.a << " " << *(a4.b.a) << endl;
}
运行结果
B的构造函数
B的构造函数
测试A是否需要赋值
a1: 00036208 1
a2: 00036238 2
调用B的赋值操作符
a1: 00036208 2
a2: 00036238 2
测试A是否复制构造
B的构造函数
a3: 000362E0 1
B的复制构造函数
a4: 00036310 1
调用B的析构函数
调用B的析构函数
调用B的析构函数
调用B的析构函数
注:默认的赋值操作符和复制构造函数递归的依赖于底层类的成员的复制和赋值的定义,它不仅仅像C版本的结构体那样仅实施按位复制,它只向内建类型成员变量实施按位复制,默认的析构函数也递归的依赖于底层类的析构函数
摘自 yucan1001
补充:软件开发 , C语言 ,