一個自定義類,如果沒有重載其=運算符,那么編譯器將自動生成一個。但是此編譯器自動生成的重載函數可能不安全。這是因為它采用了所謂的“淺拷貝”,也就是對於指針而言,它拷貝的是指針的值,而不是其指向的內存空間。那么這個內存空間就有了兩個指針指向它,且類對象要析構兩次,但是內存空間只能被釋放一次,那么第二次就會出現訪問錯誤。
要避免這種“淺拷貝”問題,就需要重載=運算符。這是一個二元運算符,函數名和參數比較容易確定,重點是其返回類型。一般來說,要返回一個類的引用類型。這是因為要能實現所謂的“鏈式編程”。我存在過的疑問是,為什么要返回一個引用,才能實現所謂的“鏈式編程”呢?這里從代碼出發,進行思考與總結。
class Student { public: Student(const char* name);~Student(); Student& operator=(const Student &student);private: char *m_name; };
//測試代碼
Student s1("Lee"); Student s2("Diwen"); Student s3("Tom");
//先執行s2 = s1, 即執行 s2.operator=(s1), 返回的是s2的引用 //然后執行s3 = s2, 即執行 s3.operator=(s2) s3 = s2 = s1;
=的鏈式編程執行順序是從右向左的。因此右邊需要返回一個Student類型的對象,是肯定的。那么問題是,為何要返回一個引用呢?
觀察一下重載=運算符的函數,參數類型是一個常量引用,也即是說傳進去的實參必須是一個左值。而如果返回類型是Student,那么返回的就是一個匿名對象(右值),作為鏈式編程,此返回值(右值)又要作為實參傳進去,那么顯然不行。那么,如果參數改成一個Student類型,是不是就可以傳匿名對象了呢?是可以的。不過如此一來,還要調用類的拷貝構造函數以初始化形參,如果沒有寫拷貝構造函數,那么對於有一個指針成員變量的Student類而言,仍是危險的。事實上也沒有什么必要把參數定為一個Student類型。
如此回答一開始的問題,這是因為作為鏈式編程,返回值將作為實參,而=運算符重載函數的形參是引用類型,則傳進去的實參必須是一個左值。那么此函數也就必須要返回引用類型才行。
---------------------------
修正:以上結論有誤。形參是引用,實參可以是匿名對象。那么返回引用的原因也僅僅是避免因返回是個對象而調用了拷貝構造函數。
---------------------------
2018-04-27,看到以前的筆記,笑了笑,真是胡言亂語了,const引用可以綁定到右值,而重載的賦值運算符返回一個引用當然是為了避免沒有意義的拷貝了。