C++拷貝建構函式和operator=
1 拷貝建構函式引數的特點
對於一個類X,如果一個建構函式的第一個引數是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
因此 X::X(X&, int=1); //是拷貝建構函式
並且類中可以存在超過一個拷貝建構函式。拷貝建構函式採用引用作引數原因:
1) 效率 。
2) 避免死迴圈。 當一個物件需要以值方式傳遞時,編譯器會生成程式碼呼叫它的拷貝建構函式以生成一個複本,因此當使用拷貝建構函式時會造成死迴圈。
2 預設拷貝建構函式
如果一個類中沒有定義拷貝建構函式,那麼編譯器會自動產生一個預設的拷貝建構函式。這個預設的引數可能為X::X(const X&)或X::X(X&),由編譯器根據上下文決定選擇哪一個。預設拷貝建構函式的行為如下(遞迴的)(預設的建構函式X()、預設的拷貝賦值函式X& operator=(X& a) 被呼叫時行為同理):
首先呼叫父類拷貝建構函式。然後拷貝建構函式對類中每一個數據成員執行成員拷貝的動作:
a) 如果資料成員為某一個類的例項,那麼呼叫此類的拷貝建構函式。
b) 如果資料成員是一個數組(或指標、引用),對陣列的每一個執行按位拷貝。
c) 如果資料成員是一個數量,如int,double,那麼呼叫系統內建的賦值運算子對其進行賦值。
注:如果父類或成員的類提供的拷貝建構函式為private,則不能通過編譯。這一點對預設的建構函式和預設的拷貝賦值函式被呼叫時同樣適用,因此最好自己定義這三個函式和解構函式。
3 拷貝建構函式呼叫時機
以下情況都會呼叫拷貝建構函式:
1) 一個物件以值傳遞的方式傳入函式體。
2) 一個物件以值傳遞的方式從函式返回。
3) 一個物件需要通過另外一個物件進行初始化。
4 理解copy constructor和copy assignment的一個例子
class callwitch{
string name ;
static int objcount;
public :
callwitch(const string& na = "") : name(na) {
objcount ++;
print("callwitch(const string&)");
};
~callwitch(){
objcount --;
print("~callwitch()");
}
callwitch(const callwitch& obj):name(obj.name) {
name = "copy of " + name;
objcount ++;
print("copy constructor");
};
callwitch& operator=(callwitch& obj){
print("copy assignment");
}
void print(const string& msg = "")const {
if(msg.size() !=0)
cout << msg << endl ;
cout << '/t' << name << ": " << "objcount = " << objcount << endl;
}
};
int callwitch::objcount = 0 ;
callwitch f(callwitch obj)
{
cout << "returning from f()" << endl;
return obj ;
}
int main(int argc, char *argv[])
{
callwitch c1("c1");
callwitch c2 = c1 ; //copy constructor called。因為此時c2還未被初始化成為物件(即還未構造出來),無法呼叫copy assignment函式!
callwitch c4 ;
c4 = c1 ; //copy assignment called
c2.print("call f()");
callwitch c3 = f(c1);
cout << "call f(),no need return value" << endl;
f(c1);
system("PAUSE");
return EXIT_SUCCESS;
}
以下是執行結果:
callwitch(int)
c1: objcount = 1
copy constructor
copy of c1: objcount = 2
callwitch(int)
: objcount = 3
copy assignment
: objcount = 3
call f()
copy of c1: objcount = 3
copy constructor
copy of c1: objcount = 4
returning from f()
copy constructor
copy of copy of c1: objcount = 5
~callwitch()
copy of c1: objcount = 4
call f(),no need return value
copy constructor
copy of c1: objcount = 5
returning from f()
copy constructor
copy of copy of c1: objcount = 6
~callwitch()
copy of copy of c1: objcount = 5
~callwitch()
copy of c1: objcount = 4
5 其它:
1) 最好自定義拷貝建構函式。如果使用編譯器生成的拷貝建構函式,呼叫拷貝建構函式時實行位拷貝,當類內成員變數需要動態開闢堆記憶體時,執行classA obj1 ; classA obj2(obj1)。如果obj1中有一個成員變數指標已經申請了記憶體,那obj2中的那個成員變數也指向同一塊記憶體。這就出現了問題:當obj1把記憶體釋放後,obj2內的指標就是無效指標了,出現執行錯誤。
2) 最好自定義operator=。如果打算在一個內含reference成員、內含const成員時,必須自己定義copy assignment操作符,因為C++本身不允許引用改指不同的物件,也不允許更改const成員;如果base class將copy assignment操作宣告為private,編譯器同樣拒絕為其derived class生成一個copy assignment操作符(effective C++ Item 6)。