關於C++重載賦值運算符"="的討論

原文地址:http://www.vckbase.com/index.php/wv/219

[問題的提出]:

在很多書籍和文章中,很多次提到在對賦值操作符(=)進行重載的時候,要返回對目的(調用)對象實例(*this)的引用。其中不免有這樣的論斷:一定要返回對調用對象的引用;返回對調用實例對象的引用是爲了實現鏈式連續賦值。

這裏說明兩個問題:第一,是否重載賦值操作符必須返回對調用對象的引用,第二,是否這樣就可以實現鏈式賦值,而不這樣就不行。

首先,必須承認,返回對"*this"的引用是標準的二目操作符重載的格式,效率很高。這樣做有很多優點:如實現鏈式賦值、避免臨時對象的產生(調用拷貝構造函數)、銷燬(調用析構函數),但不是非這樣做不可,下面通過對比來論述返回對"*this"的引用的優點及其他做法的缺點,同時也能清楚第二個問題,我們從例子着手。

01.// a.h
02.class 
03.{
04.public:
05.A();
06.A(int nTest);
07.A(const A& a);
08.virtual ~A();
09.A operator=(const A& a);
10.// A& operator=(const A& a);
11. 
12.private:
13.int m_nTest;
14. 
15.public:
16.void printit();
17.};
18.
19. 
20.// a.cpp
21.A::A(int nTest)
22.{
23.m_nTest = nTest;
24.cout << "constructor A Value is executed now!" << endl;
25.}
26. 
27.A::A(const A& a)
28.{
29.this->m_nTest = a.m_nTest;
30.cout << "Copy constructor A is executed now!" << endl; 
31.}
32. 
33.A::A()
34.{
35.cout << "constructor A Default is executed now!" << endl;
36.}
37. 
38.A::~A()
39.{
40.cout << "Destructor A is executed now!" << endl;
41.}
42.A A::operator=(const A& a)
43.// A& A::operator=(const A& a)
44.{
45.if (this==&a) 
46.return *this;
47.this->m_nTest = a.m_nTest;
48.cout << "Assignment A is 
49.executed now!" << endl;
50.return *this;
51.}

在main()函數中調用

1.A a(100),b(99),c(98);
2.a = b = c;
3.a.printit();
4.b.printit();
5.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!
98
98
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=返回的是對當前對象的引用,而引用只是別名,而不是構造新對象的。這點可以通過如下函數調用來理解:

1.void fun(A& temp)
2.{
3.temp ...
4.}
5.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; 有什麼不同?

水平有限,歡迎大家批評指正!

經實驗,回答思考題:

【1】調用A operator = (const A& a ),結果爲

【2】可以。

【3】不能 原因:不能將類型爲‘A&’的非 const 引用初始化爲類型爲‘const A*’的臨時變量

【4】

說明:上面爲A a,b; a=b的結果;下面爲A a; A b = a;的結果。上面分別調用了兩次默認構造函數和一次賦值操作;

下面調用一次默認構造函數和一次帶參數的構造函數。

發佈了22 篇原創文章 · 獲贊 54 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章