Effective C++

有人說(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)的stringcopy構(gòu)造函數(shù),所以no2.nameValue的初始化方式是調(diào)用stringcopy構(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ù)更新中......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 讓自己習(xí)慣 C++ 條款01:視 C++ 為一個(gè)語(yǔ)言聯(lián)邦 將C++視為一個(gè)由相關(guān)語(yǔ)言組成的聯(lián)邦而非單一語(yǔ)言。在...
    暗夜望月閱讀 465評(píng)論 0 1
  • 20190228暫停學(xué)習(xí),后續(xù)繼續(xù) 第一部分讓自己習(xí)慣C++ 條款1:視C++為一個(gè)語(yǔ)言聯(lián)邦 C++最初的名稱C ...
    去年匆匆今年匆匆閱讀 493評(píng)論 0 1
  • 這本書屬于“想提高必看之書”,相見恨晚,建議所有C++程序員都看看,沒事也可以拿出來(lái)翻翻。大家也可以瀏覽下面的筆記...
    拉普拉斯妖kk閱讀 771評(píng)論 0 1
  • C++在實(shí)際使用過(guò)程中確實(shí)還是有很多細(xì)節(jié)需要注意,正好最近參考google的代碼規(guī)范形成自己的代碼風(fēng)格,所以重讀e...
    寒水先生閱讀 451評(píng)論 0 0
  • 本章一共有四個(gè)條款: 視c++為一個(gè)語(yǔ)言聯(lián)邦 盡量以const,enum,inline替換#define 盡可能使...
    動(dòng)如參商_06f8閱讀 597評(píng)論 0 0

友情鏈接更多精彩內(nèi)容