有人說(shuō)C++程序員可以分為兩類,讀過(guò)《Effective C++》的和沒讀過(guò)的。無(wú)論此書是否對(duì)得起這樣的稱呼,毫無(wú)疑問(wèn)的是當(dāng)我閱讀完世界C++大師Scott Meyers成名之作之后,收獲之豐,難以言表。無(wú)論是從objected-oriented方面、template方面還是內(nèi)存管理方面,對(duì)我的整個(gè)C++觀的塑造和改變都是無(wú)與倫比的。
在國(guó)際上,本書所引起的反響,波及整個(gè)計(jì)算機(jī)技術(shù)的出版領(lǐng)域,余音至今未絕。幾乎在所有C++書籍的推薦名單上,《Effective C++》都會(huì)位居前列。作者高超的技術(shù)把握力、獨(dú)特的視角、獨(dú)具匠心的內(nèi)容組織,都受到極大的推崇和仿效。
本書不是讀完一遍就可以束之高閣的快餐讀物,也不是用以解決手邊問(wèn)題的參考手冊(cè),而是需要您去反復(fù)閱讀體會(huì)的。因此,我選擇以此博客來(lái)記錄對(duì)本書第一遍閱讀后的理解,供日后參考。相信每次閱讀都會(huì)有不一樣的感悟。毫無(wú)疑問(wèn)的是,本書一定會(huì)陪伴我整個(gè)cpp生涯。
分享一句書中的話:真正稱得上庫(kù)程序者,必然穩(wěn)健強(qiáng)固。
一、讓自己習(xí)慣C++
1、視C++為一個(gè)聯(lián)邦語(yǔ)言
一開始C++只是C加上一些面向?qū)ο筇匦?。C++最初的名稱是C With CLasses。今天的C++已經(jīng)是個(gè)多重泛型編程語(yǔ)言,一個(gè)支持過(guò)程形式、面向?qū)ο笮问?、函?shù)形式、泛型形式、元編程形式的語(yǔ)言。將C++視為一個(gè)由相關(guān)語(yǔ)言組成的聯(lián)邦而非單一語(yǔ)言有助于理解該語(yǔ)言:
- C part of C++;
- Object-Oriented C++;
- Template C++;
- STL。
無(wú)論如何請(qǐng)記?。篊++高效編程守則視狀況而變化,取決于你使用C++的哪個(gè)部分。
2、盡量以const、enum、inline替換#define
當(dāng)我們使用#define PI 3.14159265語(yǔ)句進(jìn)行宏替換時(shí),記號(hào)名稱PI可能也許從未被編譯器看見。于是當(dāng)你運(yùn)用此常量但獲得一個(gè)編譯錯(cuò)誤信息時(shí),可能會(huì)因?yàn)殄e(cuò)誤信息提到的是3.14159265而不是PI而感到困惑。解決辦法就是以一個(gè)常量替換上述宏(#define):
const double PI = 3.14159265;
值得注意的是如果要定義一個(gè)常量的char *-based字符串,必須寫const兩次:
const char * const authorName = "Scott Meyers";
當(dāng)然使用string對(duì)象通常比使用char *-based更合時(shí)宜:
const std::string authorName("Scott Meyers");
第二個(gè)值得注意的是class專屬常量:為確保常量的作用域限制于class內(nèi)且至多只有一份實(shí)體,必須讓它成為一個(gè)static成員:
class GamePlayer {
private:
static const int NumTurns = 5;
......
}
//舊式編譯器也許不支持上述語(yǔ)法,就需要將NumTurns在類外定義:
const double GamePlayer::NumTurns = 5;
當(dāng)編譯器不支持在類內(nèi)初始化static const類型的變量而你必須使用這個(gè)變量時(shí)(例如用該變量初始化數(shù)組),可以使用enum hack技術(shù)代替static const方法:
class GamePlayer {
private:
enum { NumTurns = 5 };
int scores[NumTurns];
......
}
使用enum hack好處如下:
- 行為更似#define而非const(因?yàn)閏onst可以獲取指針,而enum hack無(wú)法獲取指針);
- 許多代碼使用此技術(shù),當(dāng)看到該用法時(shí)需要認(rèn)識(shí)。enum hack是模板元編程的基礎(chǔ)。
另一個(gè)常見的#define誤用情況是以它實(shí)現(xiàn)宏函數(shù),宏函數(shù)看起來(lái)像函數(shù),但不會(huì)招致函數(shù)調(diào)用帶來(lái)的額外開銷:
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
無(wú)論何時(shí)寫出這種宏函數(shù),都必須記住為宏中的所有實(shí)參加上小括號(hào)。但是縱使加上小括號(hào),也會(huì)發(fā)生預(yù)料之外的事情:
int a = 5, b = 0;
CALL_WITH_MAX(++a, b); //a被累加兩次
CALL_WITH_MAX(++a, b + 10); //a被累加一次
取代這種做法的行為是寫出一個(gè)template inline函數(shù)代替宏函數(shù):
template<typename T>
inline void callWithMax(const T& a, const T& b) {
f(a > b ? a : b);
}
總結(jié):
- const的好處在可以追蹤報(bào)錯(cuò)及作用域控制,但能夠被選取地址;
- enum hack不僅可以追蹤報(bào)錯(cuò)及作用域控制,還不可被選取地址;
- inline不僅可以擁有宏函數(shù)的效率,還不會(huì)出現(xiàn)意想不到的錯(cuò)誤。
3、盡可能使用const
const語(yǔ)法雖然變化多端,但并不高深莫測(cè)。如果關(guān)鍵字const出現(xiàn)在星號(hào)左邊,表示被指物是常量;如果出現(xiàn)在星號(hào)右邊,表示指針自身是常量;如果出現(xiàn)在星號(hào)兩邊,表示被指物和指針兩者都是常量。
如果希望迭代器所指的東西不可被改動(dòng)(即希望STL迭代器模擬一個(gè)const T*指針),你需要的是const_iterator。在C++11標(biāo)準(zhǔn)之下,可以使用cbegin()和cend()函數(shù)代替?zhèn)鹘y(tǒng)的begin()和end()函數(shù)直接返回const_iterator迭代器,而非iterator。
令函數(shù)返回一個(gè)常量值,往往可以降低因客戶錯(cuò)誤而造成的意外,而又不至于翻譯器安全性和高效性。舉個(gè)例子:
class Rational {......};
const Rational operator* (const Rational& lhs, const Rational& rhs);
int main() {
......
if (a * b = c) //其實(shí)只是想做一個(gè)比較(==)動(dòng)作,但是少鍵入了一個(gè)=
......
}
將operator*的回傳值聲明為const可以預(yù)防那個(gè)“沒意思的賦值動(dòng)作”。至于const參數(shù),除非你有需要改動(dòng)參數(shù)或local對(duì)象,否則請(qǐng)將他們聲明為const。
在成員函數(shù)后加const,const會(huì)修飾this指針指向的對(duì)象,這就保證調(diào)用這個(gè)const成員函數(shù)的對(duì)象在內(nèi)部不會(huì)發(fā)生改變。非const對(duì)象調(diào)用非const成員函數(shù);const對(duì)象調(diào)用const成員函數(shù)。
在哲學(xué)界(手動(dòng)滑稽),有兩個(gè)流行概念:bitwise constness(又稱physical constness)和logical constness。bitwise constness陣營(yíng)的人相信,成員函數(shù)只有在不更改對(duì)象的任何成員變量(static除外)時(shí)才可以說(shuō)是const。也就是說(shuō)它不更改對(duì)象內(nèi)的任何一個(gè)bit。
C++選擇bitwise constness定義常量性。因此,const成員函數(shù)內(nèi),不可改變?cè)搶?duì)象的任何non-static成員變量。但這種方式存在缺陷:如果成員變量為指針類型,const成員函數(shù)只能保證指針的值不變,不能去報(bào)指針指向的內(nèi)容不發(fā)生改變。
所謂的logical constness,這一派主張一個(gè)const成員函數(shù)可以修改它所處理的對(duì)象內(nèi)的某些bits,但只有在客戶端偵測(cè)不出的情況下才得如此。
有些數(shù)據(jù)(例如一個(gè)bool類型的flag標(biāo)志諸如此類)的修改對(duì)用戶而言可接受,但是編譯器堅(jiān)持bitwise constness。在這種情況下就需要使用mutable關(guān)鍵字釋放non-static成員變量的bitwise constness約束!
class CTextBlock {
private:
mutable std::size_t textLength;
mutable bool LengthIsValid;
......
}
上面的這些成員變量可以被改變,即使實(shí)在cosnt成員函數(shù)內(nèi)。
如果一個(gè)類內(nèi)的const成員函數(shù)和與之對(duì)應(yīng)的非const類型的成員函數(shù),所實(shí)現(xiàn)的功能近似相同,為了減少編譯時(shí)間,避免代碼膨脹等問(wèn)題,盡可能使用const版本的函數(shù)實(shí)現(xiàn)non-const版本的函數(shù)!
class TextBlock {
public:
const char& operator[] (std::size_t position) const {
......
}
char& operator[] (std::size_t position) {
//將op[]返回值的const轉(zhuǎn)除,為*this加上const從而調(diào)用const op[]
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
}
4、確定對(duì)象被使用前已先被初始化
永遠(yuǎn)在使用對(duì)象前現(xiàn)將它初始化。對(duì)于任何無(wú)成員的內(nèi)置類型,你必須手工完成此事。至于內(nèi)置類型以外的任何其他東西,初始責(zé)任落在構(gòu)造函數(shù)身上,規(guī)則很簡(jiǎn)單:確保每一個(gè)構(gòu)造函數(shù)都將對(duì)象的每一個(gè)人員初始化。這個(gè)規(guī)則很容易奉行,重要的是別混淆了賦值(assignment)和初始化(initialization)!
class PhoneNumber {......};
class ABEntry { //Address Book Entry
private:
std::string theNames;
std::string TheAddress;
std::list<PhoneNumber> thePhones;
int numTimesConsulted;
public:
ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones){
theName = name; //這些都是賦值而非初始化
theAddress = address;
thePhones = phones;
numTimesConsulted = 0;
}
}
C++規(guī)定,對(duì)象的成員變量的初始化動(dòng)作發(fā)生在進(jìn)入構(gòu)造函數(shù)本體之前。初始化發(fā)生的時(shí)間更早,發(fā)生于這些成員的default構(gòu)造函數(shù)被自動(dòng)調(diào)用之時(shí)(比進(jìn)入ABEntry構(gòu)造函數(shù)本體的時(shí)間更早)。構(gòu)造函數(shù)的一個(gè)較佳寫法是使用所謂的member initialization list(成員初始化列表)替換賦值動(dòng)作。
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones)
:theName(name),
theAddress(address),
thePhones(phones),
numTimesConsulted(0)
{ }
使用構(gòu)造函數(shù)的初始化列表比在構(gòu)造函數(shù)內(nèi)賦值更佳且效率更高。因?yàn)榻o予賦值的那種方式會(huì)先調(diào)用default構(gòu)造函數(shù)為theName, theAddress和thePhones設(shè)置初始值,然后再在執(zhí)行ABEntry構(gòu)造函數(shù)時(shí),對(duì)這些變量賦新值。那么這些變量第一次調(diào)用的default構(gòu)造函數(shù)所做的一切都浪費(fèi)了,使用成員初始化列表就可以避免這個(gè)問(wèn)題。對(duì)于大多數(shù)類型而言,比起先調(diào)用default構(gòu)造函數(shù)再調(diào)用copy assignment操作符,單只調(diào)用一次copy構(gòu)造函數(shù)是比較高效的,有時(shí)甚至高效的多。對(duì)于內(nèi)置類型如numTimesConsulted,其初始化和賦值的成本相同,但為了一致性,最好也通過(guò)成員初始化列表來(lái)初始化。如果成員變量是const或是references,它們就一定需要初值,不能被賦值。為避免需要記住成員變量何時(shí)必須在成員初始化列表中初始化,何時(shí)不需要,最簡(jiǎn)單的方法就是:總是使用成員初始化列表。
C++有著十分固定的“成員初始化次序”:base classes更早于其derived classes被初始化,二class的成員變量總是以 其聲明次序被初始化,即使它們?cè)诔蓡T初始化列表中以不同次序出現(xiàn),也不會(huì)有任何影響。因此,在成員初始化列表中,最好總是以其聲明次序?yàn)榇涡?/strong>!
如果你對(duì)上面內(nèi)容已經(jīng)熟稔于心,那么我們還需要了解:“不同編譯單元內(nèi)定義的non-local static對(duì)象”的初始化次序。
函數(shù)內(nèi)的static對(duì)象成為local static對(duì)象,其他static對(duì)象成為non-local static對(duì)象。程序結(jié)束時(shí)static對(duì)象會(huì)被自動(dòng)銷毀,也就是它們的析構(gòu)函數(shù)會(huì)在main()結(jié)束時(shí)被自動(dòng)調(diào)用。
所謂編譯單元基本上等同于*.cpp加上*.h(同一.cpp + .h文件)。
不同編譯單元的non-local static對(duì)象的初始化順序并無(wú)明確定義,因此在上述對(duì)象在初始化過(guò)程中相互引用可能會(huì)出現(xiàn)問(wèn)題——因?yàn)橐粋€(gè)non-local static對(duì)象用到的其他編譯單元的non-local static對(duì)象可能尚未被初始化。
幸運(yùn)的是一個(gè)小設(shè)計(jì)便可消除這個(gè)問(wèn)題:將每個(gè)non-local static對(duì)象搬到自己的專屬函數(shù)內(nèi),這些函數(shù)返回一個(gè)reference指向其所含對(duì)象,然后用戶調(diào)用這些函數(shù),而不是直接指涉這些對(duì)象。換言之,non-local static對(duì)象被local static對(duì)象替換了,這是Singleton模式的一個(gè)常見實(shí)現(xiàn)手法。
Directory& tempDir() {
static Directory td; //第一次調(diào)用該函數(shù)才會(huì)執(zhí)行該語(yǔ)句,之后調(diào)用函數(shù)也不會(huì)再重復(fù)執(zhí)行。
return td;
}
這種做法還可以減少?zèng)]使用過(guò)的static對(duì)象的構(gòu)造、析構(gòu)開銷。這種單純的函數(shù)是成為inline函數(shù)的絕佳人選,尤其是它們被頻繁調(diào)用的話。擔(dān)任為了防止多線程中的不確定性,最好在開線程前就調(diào)用此函數(shù)!
二、構(gòu)造/析構(gòu)/賦值運(yùn)算
5、了解C++默默編寫并調(diào)用哪些函數(shù)
C++編譯器會(huì)自動(dòng)生成:
- 默認(rèn)構(gòu)造函數(shù);
- 拷貝構(gòu)造函數(shù);
- 析構(gòu)函數(shù);
- operator =(copy assignment)操作符。
只有上述函數(shù)被調(diào)用時(shí)才會(huì)被編譯器創(chuàng)建。
default構(gòu)造函數(shù)和析構(gòu)函數(shù)主要是給編譯器一個(gè)地方用來(lái)放置“藏身幕后”的代碼,像是調(diào)用base classes和non-static成員變量的構(gòu)造函數(shù)和析構(gòu)函數(shù)。注意編譯器產(chǎn)生的析構(gòu)函數(shù)是個(gè)non-virtual的,除非這個(gè)class的base class自身聲明有virtual析構(gòu)函數(shù)。
至于copy構(gòu)造函數(shù)和copy assignment操作符,編譯器創(chuàng)建的版本只是單純的將來(lái)源對(duì)象的每一個(gè)non-static成員變量拷貝到目標(biāo)對(duì)象。
template<typename T>
class NamedObject {
private:
std::string nameValue;
T objectValue;
......
}
NameObject<int> no2(no1); //調(diào)用自動(dòng)生成的coy構(gòu)造函數(shù)
標(biāo)準(zhǔn)的string有copy構(gòu)造函數(shù),所以no2.nameValue的初始化方式是調(diào)用string的copy構(gòu)造函數(shù)并以no1.nameValue為實(shí)參。另一個(gè)成員是int類型,那是個(gè)內(nèi)置類型,所以no2.objectValue會(huì)以“拷貝no1.objectValue內(nèi)的每一個(gè)bits”來(lái)完成初始化。
編譯器自動(dòng)生成的copy assignment操作符,其行為基本上與copy構(gòu)造函數(shù)如出一轍,但是當(dāng)含有引用成員變量,const成員變量或是基類的copy assignment操作符為private類型時(shí),編譯器不會(huì)自動(dòng)生成copy assignment操作符。
6、若不想使用編譯器自動(dòng)生成的函數(shù),就該明確拒絕
如果你不聲明copy構(gòu)造函數(shù)或copy assignment操作符,編譯器可能為你產(chǎn)出一份,于是你的class支持copying操作。如果你聲明它們,你的class還是支持copying操作,但是如果你的目的是阻止copying操作怎么辦呢?
所有編譯器產(chǎn)出的函數(shù)都是public的。為阻止這些函數(shù)被創(chuàng)建出來(lái),你得自行聲明它們。將這些函數(shù)聲明為private,可以阻止編譯器暗自創(chuàng)建其專屬版本的同時(shí),還能阻止別人調(diào)用它。一般而言這個(gè)做法并不絕對(duì)安全,因?yàn)閙ember函數(shù)和friend函數(shù)還是可以調(diào)用你的private函數(shù)。除非你只聲明這些函數(shù)而不定義它們。
將函數(shù)聲明為private卻不實(shí)現(xiàn)該函數(shù)即可達(dá)到阻止編譯器自動(dòng)創(chuàng)建且使用時(shí)報(bào)錯(cuò)的目的。
class HomeForSale {
public:
......
private:
......
HomeForSale(const HomeForSale&); //只有聲明
HomeForSale& operator= (const HomeForSale&);
};
有了上述class定義,當(dāng)客戶企圖拷貝HomeForSale對(duì)象,編譯器會(huì)阻撓他。但是如果你不慎在member函數(shù)或friend函數(shù)之內(nèi)這么做,輪到的是連接器發(fā)出抱怨。將連接期的錯(cuò)誤移至編譯期是可能的,只要將copy構(gòu)造函數(shù)和copy assignment操作符聲明為private就可以辦到,但不是在HomeForSale自身,而是在一個(gè)專門為了阻止copying動(dòng)作而設(shè)計(jì)的base class內(nèi):
class Uncopyable {
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&); //阻止copying
Uncopyable& operator= (const Uncopyable&);
};
// 為求阻止HomeForSale對(duì)象被拷貝,我們唯一需要做的就是繼承Uncopyable
class HomeForSale: private Uncopyable {
// 不再聲明copy構(gòu)造函數(shù)或copy assignment操作符
......
};
7、為多態(tài)基類聲明virtual析構(gòu)函數(shù)
工廠函數(shù)內(nèi)部都是通過(guò)動(dòng)態(tài)申請(qǐng)的方式創(chuàng)建一個(gè)派生類的對(duì)象,因此該對(duì)象位于堆段。為了避免內(nèi)存泄露,使用完后,必須將工廠函數(shù)返回的指針對(duì)象釋放(可以通過(guò)智能指針shared_ptr解決)。由一個(gè)含有非虛析構(gòu)函數(shù)的基類指針銷毀派生類對(duì)象時(shí)會(huì)出現(xiàn)派生類對(duì)象中派生類成分未被銷毀的情況,因?yàn)榕缮惖奈鰳?gòu)函數(shù)很可能并未執(zhí)行(具體是否執(zhí)行完全隨機(jī))。消除這個(gè)問(wèn)題的做法非常簡(jiǎn)單:給基類的析構(gòu)函數(shù)聲明為virtual析構(gòu)函數(shù)即可解決。
任何class只要帶有virtual函數(shù)都幾乎確定應(yīng)該也有一個(gè)virtual析構(gòu)函數(shù)。如果class不含virtual函數(shù),通常表示它并不意圖被用做一個(gè)base class。當(dāng)class不企圖被當(dāng)做base class,令析構(gòu)函數(shù)為virtual函數(shù)往往是個(gè)餿主意。凡是含有虛函數(shù)的class,都會(huì)增加一個(gè)指針vptr(virtual table pointer,虛表指針)用來(lái)指向一個(gè)由函數(shù)指針構(gòu)成的數(shù)組vtbl(virtual table,虛函數(shù)表)。vptr指針長(zhǎng)度為四個(gè)字節(jié)(具體大小跟編譯器和計(jì)算機(jī)架構(gòu)有關(guān)),而且放置于class的最前端內(nèi)存處,同時(shí)存在字節(jié)對(duì)齊的問(wèn)題。因此,使用虛函數(shù)會(huì)導(dǎo)致class占用的內(nèi)存增加,可移植性降低。
如果不希望帶non-virtual析構(gòu)函數(shù)的class被其他class繼承,可以使用C++11引入的final關(guān)鍵字,阻止繼承。
將析構(gòu)函數(shù)聲明為純虛函數(shù)可以一舉兩得:使當(dāng)前類成為抽象類且不需要在提供額外的純虛函數(shù)!但謹(jǐn)記,仍要為析構(gòu)函數(shù)實(shí)現(xiàn)一份定義,否則派生類在調(diào)用基類析構(gòu)函數(shù)時(shí)會(huì)因?yàn)榛悰]有定義析構(gòu)函數(shù)而報(bào)錯(cuò)!
8、別讓異常逃離析構(gòu)函數(shù)
如果析構(gòu)函數(shù)能夠拋出異常,那么C++編譯器在delet []一組數(shù)據(jù),調(diào)用一組析構(gòu)函數(shù)時(shí),就有可能同時(shí)拋出一個(gè)或一個(gè)以上的異常。但是對(duì)C++而言,在兩個(gè)異常同時(shí)存在的情況下,程序若不是結(jié)束執(zhí)行就是導(dǎo)致不明確行為。當(dāng)然,容器或array并非遇上麻煩的必要條件,只要析構(gòu)函數(shù)突出異常,即使并非使用容器或array,程序也可能過(guò)早結(jié)束或出現(xiàn)不明確行為。
因此,將類似數(shù)據(jù)庫(kù)的close的行為專門移交給一個(gè)成員函數(shù)并交給用戶調(diào)用,讓析構(gòu)函數(shù)盡可能少做可能拋出異常的事情。給用戶給自己調(diào)用close函數(shù)并不會(huì)增加負(fù)擔(dān),反而還提供了處理異常的機(jī)會(huì)。析構(gòu)函數(shù)close只是雙保險(xiǎn),但異常發(fā)生時(shí)必須退出程序或吞下異常。
9、絕不在構(gòu)造和析構(gòu)過(guò)程中調(diào)用virtual函數(shù)
你不該在構(gòu)造函數(shù)和析構(gòu)函數(shù)期間調(diào)用virtual函數(shù),因?yàn)檫@樣的調(diào)用不會(huì)帶來(lái)你預(yù)想的結(jié)果,就算有你也不會(huì)高興!
由于base class構(gòu)造函數(shù)的執(zhí)行更早于derived class構(gòu)造函數(shù),當(dāng)base class構(gòu)造函數(shù)執(zhí)行時(shí)derived class的成員變量尚未初始化。如果再次期間調(diào)virtual函數(shù)下降至derived class階層,要知道derived class的函數(shù)計(jì)劃必然取用local成員變量,而那些成員變量尚未初始化。這將是一張通往不明確行為和徹夜調(diào)試大會(huì)的直通車票。
其實(shí)還有比上述理由更根本的原因:在derived class對(duì)象的base class構(gòu)造期間,對(duì)象的類型是base class而不是derived class。不只virtual函數(shù)會(huì)被編譯器解析至base class,若使用運(yùn)行期類型信息,也會(huì)把對(duì)象視為base class類型。
相同的道理也適用于析構(gòu)函數(shù)。一旦derived class析構(gòu)函數(shù)開始執(zhí)行,對(duì)象內(nèi)的derived class成員變量便呈現(xiàn)未定義值,所以C++視它們仿佛不再存在。進(jìn)入base class析構(gòu)函數(shù)后對(duì)象就成為一個(gè)base class 對(duì)象,而C++的任何部分包括virual函數(shù)、dynamic_casts等等也就那么看待它。
在構(gòu)造和析構(gòu)期間不要調(diào)用vitual函數(shù),因?yàn)檫@類調(diào)用從不下降至derived class!
10、令operator =返回一個(gè)reference to *this
關(guān)于復(fù)制,有趣的是你可以把它們攜程連鎖形式x = y = z = 15;,賦值采用右結(jié)合律,所以上述連鎖賦值被解析為x = (y = (z = 15));。
為了實(shí)現(xiàn)“連鎖賦值”,賦值操作符必須返回一個(gè)reference指向操作符的左側(cè)實(shí)參。這是classes實(shí)現(xiàn)賦值操作符時(shí)一個(gè)遵循的協(xié)議:
class Widget {
public:
Widget & operator = (const Widget &rhs) {
......
return * this;
}
};
這個(gè)協(xié)議不僅適用于以上標(biāo)準(zhǔn)賦值形式,也適用于所有賦值相關(guān)運(yùn)算符。
11、在operator=中處理“自我賦值”
持續(xù)更新中......