1 class Widget{ 2 3 Widget(); //默認構造函數 4 5 Widget(const Widget& rhs); //復制構造函數 6 7 Widget& operator= (const Widget& rhs);//賦值操作函數 8 9 }; 10 Widget w1; //調用默認構造函數 11 Widget w2(w1); //調用復制構造函數 12 w1 = w2 ; //調用賦值操作函數
上面的語句很好理解,但是需要我們注意的是“=”也可以用來調用復制構造函數。
Widget w3 = w2;
此時調用的是復制構造函數,如果一個新對象被定義,一定會有一個構造函數被調用,例如上面的w3,不可能調用賦值操作。如果沒有新的對象被定義(例如上面的 w1=w2)就不會有構造函數被定義,那麼當然就是賦值操作被調用。
這是因為類有一個默認的賦值操作符,雖然你沒寫,編譯器已經為你自動生成了,大概如下:
A& operator=(A& a)
{
this->XXX = a.XXX; //把a的成員變量值都拷貝給這個對象的成員變量
....
return *this;
}
ps.如果你的類裡面有指針成員變量,你就必須重寫拷貝構造函數和賦值操作符。因為你不重寫的話,就會調用默認的拷貝構造函數和賦值操作符,而默認的是成員變量按位拷貝,所以就會導致兩個類對象的指針變量同時指向一塊內存。
下面是一道很經典的面試題,就是關於這個問題的。在這題裡面,就必須重寫拷貝構造函數和賦值操作符。你可以自己寫下,對你理解這個很有幫助。
已知類String的原型為:
class String
{
public:
String(const char *str = NULL); // 普通構造函數
String(const String &other); // 拷貝構造函數
~ String(void); // 析構函數
String & operate =(const String &other); // 賦值函數
private:
char *m_data; // 用於保存字符串
};
請編寫String的上述4個函數。
復制構造函數接受單個類類型引用形參,這個形參一般用const修飾。
class A
{ public:
A();;
A(const A&);
//.........
};
一般的類,編譯器合成的構造函數就能完成必要的工作。擔當類有一個數據成員是指針,或者成員表示在構造函數中分配的其他資源;在創建新對象時必須做一些特定工作。這兩種情況必須定義復制構造函數。
復制操作符重載,由operator後面跟所定義的操作符符號,通過定義名為operator=函數來對賦值進行定義。該操作符函數有兩個形參:第一個形參對應左邊的操作數(隱式綁定到this指針了),第二個形參對應右操作數。返回類型應該與內置賦值運算返回的類型相同,內置類型的賦值運算返回對右操作數的引用,賦值操作符也返回對同一類型的引用。
class B
{
public:
B& operator=(const B &);
};
可以使用合成復制構造函數的類通常也可以使用合成賦值操作符。一般來說,如果類需要復制構造函數,也就需要賦值操作符。
OK……