假設我們要將一個元素類型為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