[問題的提出]:
在很多書籍和文章中,很多次提到在對賦值操作符(=)進行重載的時候,要返回對目的(調用)對象實例(*this)的引用。其中不免有這樣的論斷:一定要返回對調用對象的引用;返回對調用實例對象的引用是為了實現鏈式連續賦值。
這裡說明兩個問題:第一,是否重載賦值操作符必須返回對調用對象的引用,第二,是否這樣就可以實現鏈式賦值,而不這樣就不行。
首先,必須承認,返回對"*this"的引用是標准的二目操作符重載的格式,效率很高。這樣做有很多優點:如實現鏈式賦值、避免臨時對象的產生(調用拷貝構造函數)、銷毀(調用析構函數),但不是非這樣做不可,下面通過對比來論述返回對"*this"的引用的優點及其他做法的缺點,同時也能清楚第二個問題,我們從例子著手。
// a.h
class A
{
public:
A();
A(int nTest);
A(const A& a);
virtual ~A();
A operator=(const A& a);
// A& operator=(const A& a);
private:
int m_nTest;
public:
void printit();
};
}
// a.cpp
A::A(int nTest)
{
m_nTest = nTest;
cout << "constructor A Value is executed now!" << endl;
}
A::A(const A& a)
{
this->m_nTest = a.m_nTest;
cout << "Copy constructor A is executed now!" << endl;
}
A::A()
{
cout << "constructor A Default is executed now!" << endl;
}
A::~A()
{
cout << "Destructor A is executed now!" << endl;
}
A A::operator=(const A& a)
// A& A::operator=(const A& a)
{
if (this==&a)
return *this;
this->m_nTest = a.m_nTest;
cout << "Assignment A is
executed now!" << endl;
return *this;
}
在main()函數中調用
A a(100),b(99),c(98);
a = b = c;
a.printit();
b.printit();
c.printit();
結果為:
constructor A Value is executed now!
constructor A Value is executed now!
constructor A Value is executed now!
Assignment A is executed now!
Copy constructor A is executed now!
Assignment A is executed now!
Copy constructor A is executed now!
Destructor A is executed now!
Destructor A is executed now!
99
99
98
Destructor A is executed now!
Destructor A is executed now!
Destructor A is executed now!
如果將 A operator=(const A& a) 改為 A& operator=(const A& a)
則結果為:
constructor A Value is executed now!
constructor A Value is executed now!
constructor A Value is executed now!
Assignment A is executed now!
Assignment A is executed now!
98
98
98
Destructor A is executed now!
Destructor A is executed now!
Destructor A is executed now!
兩者的不同為前者比後者多執行了兩次構造(拷貝構造函數)和析構函數,可見在執行過程充產生了兩個臨時對象。
[1]在賦值函數為:A operator=(const A& a)的情況下
對於a=b=c; 實際為a.operator=(b.operator=(c))
在執行A operator=(const A& a) 後返回 *this 給一個臨時對象,所以生成和銷毀這個臨時對象的時候分別要調用構造和析構函數,而構造時是用一個已經存在的實例出初始化同類型的實例,所以調用的拷貝初始化函數。析構時,先析構前面一個(a.operator=)產生的臨時對象,後析構"b.operator="產生的臨時對象.
[2] 在賦值函數為:A& operator=(const A& a)的情況下
不同的是沒有臨時對象的產生,因為operator=返回的是對當前對象的引用,而引用只是別名,而不是構造新對象的。這點可以通過如下函數調用來理解:
void fun(A& temp)
{
temp ...
}
A a;
執行fun(a)函數調用時,沒有產生臨時對象。
可見,重載"="操作符,不一定要返回對賦值目的對象的引用,但返回引用是很好的做法,建議您這樣使用。
最後提出幾個問題,大家可以思考一下:
[1] 若將a=b=c; 改為(a=b)=c後分別調用A operator=(const A& a) 和A&operator=(const A& a)結果會有什麼不同?
[2] 能否將A&operator=(const A& a)改為const A&operator=(const A& a)?
[3] 能否將A&operator=(const A& a)中的return *this;改為return a?
[4] A a, b;
a = b;
與 A a;
A b = a; 有什麼不同?
水平有限,歡迎大家批評指正!
本文配套源碼