首先明確一點:
系統已經提供了預設的 拷貝建構函式 和 =複製運算子。 即所謂的淺拷貝。
但有時,我們必須提供自己重寫。一般是在有指標的情況下重寫。
舉個簡單的例子,沒有指標,其實不必重寫,只是為了示範:
class Fraction{private:int fenmu; //分母int fenzi; //分子public:Fraction(int x,int y){fenzi = x;fenmu = y;}Fraction(){}Fraction(const Fraction & fr);Fraction & operator=(Fraction& p);void display(){cout << fenmu << " " << fenzi;}};Fraction::Fraction(const Fraction & fr){cout << "test: use copy" << endl;fenmu = fr.fenmu;fenzi = fr.fenzi;}Fraction & Fraction::operator=(Fraction& fr){if(this == &fr)return *this;fenmu = fr.fenmu;fenzi = fr.fenzi;cout << "test use =" << endl;return *this;}int main(){Fraction f(1,2);Fraction f2(f); //use copy//f2.display();Fraction f3 = f2; // use copyFraction f4,f5;f5 = f4 = f3; // use =//f5.display();return 0;}
output:
test: use copytest: use copytest use =test use =
如果有指標,則需要深拷貝:
#include <iostream>using namespace std;class CA{ public: CA(int b,char* cstr) { a=b; str=new char[b]; strcpy(str,cstr); } CA(const CA& C) { a=C.a; str=new char[a]; //深拷貝 if(str!=0) strcpy(str,C.str); } void Show() { cout<<str<<endl; } ~CA() { delete str; } private: int a; char *str;};int main(){ CA A(10,"Hello!"); CA B=A; B.Show(); return 0;}
通過拷貝相同類的另一個對象的狀態來初始化一個對象。
使用:當通過數值傳遞,通過數值返回,或明確拷貝一個對象
賦值運算子
返回什麼:一般通過引用返回*this,就是說使用者自己定義的類的賦值遵守和內部類型相同的約定,賦值也可用作運算式,也即能夠級聯
自我賦值文法上沒有任何錯誤,但如果你沒有很好的實現賦值運算子,那麼災難可能就在等著你。
所以你必須確保自我賦值無害,也就是要加入自我賦值的檢測
CAssignment& CAssignment::operator=(const CAssignment& a){ if( this == &a ) return *this; //….賦值該作的工作}
保證賦值運算子只有兩種結果:完全成功、原封不動留下對象並拋出異常
CAssignment& CAssignment::operator=(const CAssignment& a){ if( this == &a ) return *this; CTemp* t = new CTemp; //….. delete _tmp; _tmp = t; return *this;}
衍生類別使用賦值運算子
衍生類別中的賦值運算子首先調用其直接基類賦值運算子(對聲明在基類裡的成員對象賦值),然後再調用它的成員對象的賦值運算子(改變在衍生類別裡聲明的那些成員對象)。這些賦值通常應該與基類和成員對象在該類的定義裡出現的次序相同。
CDerived& CDerived::operator=(const CDerived& r){CBase::operator=(r);_c = r._c;return *this;}
重載賦值運算子的正確形式:
c++的設計者stroustrup下了很大的功夫想使使用者自訂類型儘可能地和內部
類型的工作方式相似。為此他做了很多努力(如重載運算子,寫類型轉換函
數和拷貝建構函式,等等)。而你也該繼續做下去。
讓我們看看賦值。用內部類型的情況下,賦值操作可以象下面這樣鏈起來:
int w, x, y, z; w = x = y = z = 0;
所以,你也應該可以將使用者自訂類型的賦值操作鏈結起來:
CString w, x, y, z; // MFC “自訂”的類型
w = x = y = z = "hello";
因為賦值運算子的結合性天生就是由右向左,所以上面的賦值可以解析為:
w = (x = (y = (z = "hello"))); <=>
w.operator=(x.operator=(y.operator=(z.operator=("hello"))));
這個格式說明了w.operator=, x.operator=和y.operator=的參數是前一個
operator=調用的傳回值。所以operator=的傳回值必須可以作為一個輸
入參數被函數自己接受。一般情況下operator=輸入應該是類對象或
類對象的引用,從效率來說後者好過前者 ,所以輸入和返回的都應
是類對象的引用。
又因為有
int x, y,z; (x = y) = z ;
所以如下語句也應正確
CString x, y, z; ( x = y) = z;
那麼operator=的返回不能是const(因為常量不能賦左值)
又有
CString x; x = “Hello”; <=>
const CString temp(“Hello”); //產生臨時對象,為什麼是const
x = temp;
所以為保證上面的語句成立, operator=的輸入應是const
所以最好的實現是 T& operator=(const T&);