首先明確一點:
系統已經提供了默認的 拷貝構造函數 和 =復制運算符。 即所謂的淺拷貝。
但有時,我們必須提供自己重寫。一般是在有指針的情況下重寫。
舉個簡單的例子,沒有指針,其實不必重寫,只是為了演示:
[cpp]
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 copy
Fraction f4,f5;
f5 = f4 = f3; // use =
//f5.display();
return 0;
}
output:
[cpp]
test: use copy
test: use copy
test use =
test use =
如果有指針,則需要深拷貝:
[cpp] view plaincopyprint?
#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,就是說用戶自己定義的類的賦值遵守和內部類型相同的約定,賦值也可用作表達式,也即能夠級聯
自我賦值語法上沒有任何錯誤,但如果你沒有很好的實現賦值運算符,那麼災難可能就在等著你。
所以你必須確保自我賦值無害,也就是要加入自我賦值的檢測
[cpp]
CAssignment& CAssignment::operator=(const CAssignment& a)
{
if( this == &a )
return *this;
//….賦值該作的工作
}
保證賦值運算符只有兩種結果:完全成功、原封不動留下對象並拋出異常
[cpp]
CAssignment& CAssignment::operator=(const CAssignment& a)
{
if( this == &a )
return *this;
CTemp* t = new CTemp;
//…..
delete _tmp;
_tmp = t;
return *this;
}
派生類使用賦值運算符
派生類中的賦值運算符首先調用其直接基類賦值運算符(對聲明在基類裡的成員對象賦值),然後再調用它的成員對象的賦值運算符(改變在派生類裡聲明的那些成員對象)。這些賦值通常應該與基類和成員對象在該類的定義裡出現的次序相同。
[cpp]
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(因為常量不能賦左值)
又有 www.2cto.com
CString x; x = “Hello”; <=>
const CString temp(“Hello”); //產生臨時對象,為什麼是const
x = temp;
所以為保證上面的語句成立, operator=的輸入應是const
所以最好的實現是 T& operator=(const T&);