EffectiveC++(2)----構(gòu)造/析構(gòu)/賦值運(yùn)算-創(chuàng)新互聯(lián)

條款05:了解C++默默編寫并調(diào)用了哪些函數(shù)

若寫下

創(chuàng)新互聯(lián)服務(wù)緊隨時(shí)代發(fā)展步伐,進(jìn)行技術(shù)革新和技術(shù)進(jìn)步,經(jīng)過(guò)10余年的發(fā)展和積累,已經(jīng)匯集了一批資深網(wǎng)站策劃師、設(shè)計(jì)師、專業(yè)的網(wǎng)站實(shí)施團(tuán)隊(duì)以及高素質(zhì)售后服務(wù)人員,并且完全形成了一套成熟的業(yè)務(wù)流程,能夠完全依照客戶要求對(duì)網(wǎng)站進(jìn)行成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、建設(shè)、維護(hù)、更新和改版,實(shí)現(xiàn)客戶網(wǎng)站對(duì)外宣傳展示的首要目的,并為客戶企業(yè)品牌互聯(lián)網(wǎng)化提供全面的解決方案。
class Empty { };

當(dāng)C++處理過(guò)它之后,編譯器就會(huì)為它聲明一個(gè)default構(gòu)造函數(shù),一個(gè)copy構(gòu)造函數(shù),一個(gè)copy assignment操作符和一個(gè)析構(gòu)函數(shù)。這就好像寫下這樣的代碼:

class Empty {
public:
    Empty() { ... }
    Empty(const Empty& rhs) { ... }
    ~Empty() { ... }
    Empty& operator=(const Empty& rhs) { ... }
}

惟有這些函數(shù)被需要(被調(diào)用),它們才會(huì)被編譯器創(chuàng)建出來(lái)。

Empty e1;                     //default構(gòu)造函數(shù)
Empty e2(e1);                 //copy構(gòu)造函數(shù)
e2 = e1;                      //copy assignment操作符

default構(gòu)造函數(shù)和析構(gòu)函數(shù)主要是給編譯器一個(gè)地方來(lái)放置“藏身幕后”的代碼,像是調(diào)用base classesnon-static成員變量的構(gòu)造函數(shù)和析構(gòu)函數(shù)。注意,編譯器產(chǎn)出的析構(gòu)函數(shù)是個(gè)non-virtual。copy構(gòu)造函數(shù)和copy assignment操作符,編譯器創(chuàng)建的版本只是單純地將來(lái)源于對(duì)象的每一個(gè)non-static成員變量拷貝到目標(biāo)對(duì)象。

templateclass NameObject {
public:
    NameObject(const char* name, const T& value);
    NameObject(const std::string name, const T& value);
private:
    std::string nameValue;
    T objectValue;
};

NameObjectno1("Smallest Prime Number", 2);
NameObjectno2(no1);

編譯器生成的copy構(gòu)造函數(shù)必須以no1.nameValue和no1.objectValue為初值設(shè)定no2.nameValue和no2.objectValue。nameValue的類型是string,調(diào)用string的copy構(gòu)造函數(shù)。objectValue的類型是int,是個(gè)內(nèi)置類型,所以會(huì)拷貝每一個(gè)bits來(lái)完成初始化。

若NameObject的定義如下,其中的nameValue是個(gè)referencetostring,objectValue是個(gè)const T:

templateclass NameObject {
public:
    NameObject(std::string& name, const T& value);
private:
    std::string &nameValue;             //是個(gè)reference
    const T objectVaule;                //是個(gè)const
};

std::string newDog("Persephone");
std::string oldDog("Satch");
NameObjectp(newDog, 2);
NameObjects(oldDog, 36);
p = s;                                // p的成員變量該發(fā)生什么事?

編譯器拒絕編譯賦值動(dòng)作。

如果某個(gè)base classes將copy assignment操作符聲明成private,編譯器將拒絕為derived classes生成一個(gè)copy assignment操作符。

請(qǐng)記?。?/p>

編譯器可以暗自為class創(chuàng)建default構(gòu)造函數(shù)、copy構(gòu)造函數(shù)、copy assignment操作符,以及析構(gòu)函數(shù)。

條款06:若不想使用編譯器自動(dòng)生成的函數(shù),就該明確拒絕

用一個(gè)class描述待售房屋

class HomeForSale { ... };

每個(gè)售賣的房屋都是獨(dú)一無(wú)二的,所以HomeForSale對(duì)象不能被復(fù)制。

HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1);          //企圖拷貝h1,不應(yīng)通過(guò)編譯
h1 = h2;                     //企圖拷貝h2,不應(yīng)通過(guò)編譯

如果不聲明copy構(gòu)造函數(shù)或copyassignment操作符,編譯器可能會(huì)為你產(chǎn)出一份,支持copying;

如果聲明了copy構(gòu)造函數(shù)或copyassignment操作符,那就還是支持copying

所有編譯器產(chǎn)出的函數(shù)都是public,我們可以把copy構(gòu)造函數(shù)和copyassignment操作符聲明成private。但是成員函數(shù)和友元函數(shù)還是可以調(diào)用private函數(shù),解決辦法就是“將成員函數(shù)聲明為private而且故意不實(shí)現(xiàn)它們”。

class HomeForSale {
private:
    HomeForSale(const HomeForSale&);
    HomeForSale& operator=(const HomeForSale &);
};

請(qǐng)記?。?/p>

為駁回編譯器自動(dòng)(暗自)提供的機(jī)能,可將相應(yīng)的成員函數(shù)聲明為private并且不予實(shí)現(xiàn)。

條款07:為多態(tài)基類聲明virtual析構(gòu)函數(shù)
class TimeKeeper {
public:
    TimeKeeper();
    ~TimeKeeper();
};

class AtomicClock : public TimeKeeper { ... };
class WaterClock : public TimeKeeper { ... };
class WristWatch : public TimeKeeper { ... };

// 使用工廠模式函數(shù),返回一個(gè)指向derived class對(duì)象的base class指針
TimerKeeper* ptk = getTimeKeeper();
...
delete ptk;

C++明確指出,當(dāng)derived class對(duì)象經(jīng)由一個(gè)base class指針被刪除,而該base class帶著一個(gè)non-virtual析構(gòu)函數(shù),其結(jié)果未有定義——實(shí)際執(zhí)行時(shí)通常發(fā)生的是對(duì)象的derived成分沒(méi)被銷毀。解決問(wèn)題辦法很簡(jiǎn)單,給base class一個(gè)virtual的析構(gòu)函數(shù)。

class TimeKeeper {
public:
    TimeKeeper();
    virtual ~TimeKeeper();
};

TimeKeeper *ptk = getTimeKeeper();
...
delete ptk;

任何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往往是個(gè)餿主意。

class Point {
public:
    Point(int xCoord, int yCoord);
    ~Point();
private:
    int x, y;
};

欲實(shí)現(xiàn)出virtual函數(shù),對(duì)象必須攜帶某些信息,主要用來(lái)控制運(yùn)行期決定哪一個(gè)virtual函數(shù)該被調(diào)用。這份信息通常是由一個(gè)所謂vptr(vitrual table pointer)指針指出。vptr指向一個(gè)由函數(shù)指針構(gòu)成的數(shù)組,成為vtbl(vitrual table);每一個(gè)帶有virtual函數(shù)的class都有一個(gè)對(duì)應(yīng)的vtbl。當(dāng)對(duì)象調(diào)用某一virtual函數(shù),實(shí)際調(diào)用的函數(shù)取決于該對(duì)象的vptr所指的那個(gè)vtbl——編譯器在其中尋找適當(dāng)?shù)暮瘮?shù)指針。

若Point class內(nèi)含virtual函數(shù),其對(duì)象的體積會(huì)增加。

std::string,STL中容器如vector,list,set等等都不帶有virtual析構(gòu)函數(shù),不要繼承一個(gè)標(biāo)準(zhǔn)容器或其他“帶有non-virtual析構(gòu)函數(shù)”的class。

純虛函數(shù)(pure virtual),擁有純虛函數(shù)的類是抽象類。

class AWON {
public:
    virtual ~AWON() = 0;
};

析構(gòu)函數(shù)的運(yùn)作方式是,最深層派生(most derived)的那個(gè)class其析構(gòu)函數(shù)最先被調(diào)用,然后是其每一個(gè)base class的析構(gòu)函數(shù)被調(diào)用。?編譯器會(huì)在AWON的derived classes的析構(gòu)函數(shù)中創(chuàng)建一個(gè)對(duì)~AWON的調(diào)用動(dòng)作,所以必須為這個(gè)函數(shù)提供一份定義,否則鏈接器會(huì)發(fā)出抱怨。

請(qǐng)記住:

帶有多態(tài)性質(zhì)的base classes應(yīng)該聲明一個(gè)virtual的析構(gòu)函數(shù)。如果class帶有任何的virtual函數(shù),那么它就應(yīng)該擁有一個(gè)virtual析構(gòu)函數(shù)。

classes的設(shè)計(jì)目的如果不是作為base classe使用,或不是為了具備多態(tài)性,就不應(yīng)該聲明virtual析構(gòu)函數(shù)。

條款09:絕不在構(gòu)造和析構(gòu)過(guò)程中調(diào)用virtual函數(shù)
class Transaction {
public:
    Transaction();
    virtual void logTransaction() const = 0;
    ...
};

Transaction::Transaction() {
    ...
    logTransaction();
}

class BuyTransaction : public Transaction {
public:
    virtual void logTransaction() const;
};

class SellTransaction : public Transaction {
public:
    virtual void logTransaction() const;
};

BuyTransaction b;            //會(huì)發(fā)生什么?

BuyTransaction的構(gòu)造函數(shù)被調(diào)用,但首先Transaction構(gòu)造函數(shù)一定會(huì)更早被調(diào)用;Transaction構(gòu)造函數(shù)的最后一行調(diào)用了virtual函數(shù)logTransaction,調(diào)用的是Transaction內(nèi)的版本,而不是BuyTransaction內(nèi)的版本。base class的構(gòu)造期間virtual函數(shù)是不會(huì)下降到derived class階層。同樣道理也適用于析構(gòu)函數(shù)。

class Transaction {
public:
    explicit Transaction(const std::string& logInfo);
    void logTransaction(const std::string& logInfo);
};

Transaction::Transaction(const std::string& logInfo) {
    logTransaction(logInfo);
}

class BuyTransaction : public Transaction {
public:
    BuyTransaction(parameters) : Transaction(createLogString(parameters)) {

    }
private:
    static std::string createLogString(parameters);
};

可以由derived classes將必要的信息向上傳遞給至base class的構(gòu)造函數(shù)。BuyTransaction內(nèi)的private static函數(shù)作用,此函數(shù)是static函數(shù),就不可能意外指向“初期未成熟之BuyTransaction對(duì)象內(nèi)尚未初始化的成員變量”。?

請(qǐng)記?。?/p>

在構(gòu)造和析構(gòu)期間不要調(diào)用virtual函數(shù),因?yàn)檫@類調(diào)用從不下降到derived class。

條款10:令operator=返回一個(gè)referenceto*this

關(guān)于賦值,可以寫成連鎖形式

int x,y,z;
x = y = z = 15;

賦值采用的是右結(jié)合,所以上述賦值被解析為:

x = (y = (z = 15));

15先賦值給z,然后其結(jié)果(更新后的z)再賦值給y,然后其結(jié)果(更新后的y)再賦值給x。

為了實(shí)現(xiàn)連續(xù)賦值,賦值操作符必須返回一個(gè)reference指向操作符的左側(cè)實(shí)參。

class Widget {
public:
    Widget& operator=(const Widget& rhs) {
        ...
        return *this;
    }

    Widget& operator+=(const Widget& rhs) {
        ...
        return *this;
    }
};

請(qǐng)記?。?/p>

令賦值操作符返回一個(gè)reference to*this。?

條款11:在operator=中處理“自我賦值”
class Widget { ... }
Widget w;
w = w;

看起來(lái)有點(diǎn)愚蠢,但它合法。

a[i] = a[j];   //當(dāng)i和j有相同值時(shí)
*px = *py;     //當(dāng)px和py指向相同的東西
class Bitmap { ... };

class Widget {
private:
    Bitmap* pb;
};

Widget& Widget::operator=(const Widget& rhs) {
    delete pb;                            //刪除原來(lái)的pb
    pb = new Bitmap(*rhs.pb);             //令pb指向*pb的一個(gè)副本
    return *this;
}

增加“證同測(cè)試”

Widget& operator=(const Widget& rhs) {
    if(this == &rhs) return *this;
    ...
}

當(dāng)new Bitmap拋出異常時(shí),pb保持原狀。即使沒(méi)有證同測(cè)試,這段代碼還是能夠處理自我賦值,雖然不是效率最高,但是它行的通。

Widget& Widget::operator=(const Widget& rhs) {
    Bitmap* pOrg = pb;                    //記住原來(lái)的pb
    pb = new Bitmap(*rhs.pb);             //令pb指向*pb的一個(gè)副本
    delete pOrg;                          //刪除原來(lái)的pb
    return *this;
}

一個(gè)常見(jiàn)而夠好的operator=撰寫方法如下:

class Widgt {
...
void swap(Widget& rhs);     //交互*this和rhs的數(shù)據(jù)
...
};

Widget& Widget::operator=(const Widget& rhs) {
    Widget temp(rhs);
    swap(temp);
    return *this;
}

另外一個(gè)變奏,是用以by value方式接受實(shí)參

Widget& Widget::operator=(Widget rhs) {   //rhs是被傳遞對(duì)象的一個(gè)副本
    swap(rhs);                            //將*this的數(shù)據(jù)和副本的數(shù)據(jù)交換
    return *this;
}

請(qǐng)記?。?/p>

確保當(dāng)對(duì)象自我賦值時(shí)operator=有良好的行為。其中技術(shù)包括比較“來(lái)源對(duì)象”和“目標(biāo)對(duì)象”的地址、精心周到的語(yǔ)句順序、以及copy-and-swap。

確認(rèn)任何函數(shù)如果操作一個(gè)以上的對(duì)象,而其中多個(gè)對(duì)象是同一個(gè)對(duì)象時(shí),其行為仍然正確。

條款12:賦值對(duì)象時(shí)勿忘其每一個(gè)成分
void logCall(const std::string& funcName);

class Customer {
public:
    Customer(const Customer& rhs);
    Customer& operator=(const Customer& rhs);
private:
    std::string name;
};

Customer::Customer(const Customer& rhs) : name(rhs.name) {
    logCall("Customer copy constructor");
}

Customer& Customer::operator=(const Customer& rhs) {
    logCall("Customer copy assignment operator");
    name = rhs.name;
    return *this;
}

當(dāng)新增一個(gè)Date成員后:

class Date { ... };

class Customer {
public:
...
private:
    std::string name;
    Date lastTransaction;
};

這時(shí)既有的copying函數(shù)執(zhí)行的是局部拷貝:它們的確復(fù)制了name,但是沒(méi)有復(fù)制新增的lastTransaction。編譯器不會(huì)提醒你。如果為class增加了一個(gè)成員變量,必須同時(shí)修改copying函數(shù)。

一旦發(fā)生繼承,可能會(huì)造成一個(gè)潛藏危機(jī)。

class PriorityCustomer : public Customer {
public:
    ...
    PriorityCustomer(const PriorityCustomer& rhs);
    PriorityCustomer& operator=(const PriorityCustomer& rhs);
private:
    int priority;
};

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs) : priority(rhs.priority) {
    logCall("PriorityCustomer copy constructor");
}

PriorityCustomer& operator=::PriorityCustomer(const PriorityCustomer& rhs) {
    logCall("PriorityCustomer copy constructor");
    priority = rhs.priority;
    return *this;
}

PriorityCustomer的copying函數(shù)看起來(lái)好像是復(fù)制了PriorityCustomer內(nèi)的每樣?xùn)|西,再看一眼,它們復(fù)制了PriorityCustomer聲明的成員變量,對(duì)繼承自Customer的成員變量卻未被復(fù)制。

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs) : 
    Customer(rhs), priority(rhs.priority) {
    logCall("PriorityCustomer copy constructor");
}

PriorityCustomer& operator=::PriorityCustomer(const PriorityCustomer& rhs) {
    logCall("PriorityCustomer copy constructor");
    Customer::operator=(rhs);
    priority = rhs.priority;
    return *this;
}

請(qǐng)記?。?/p>

Copying函數(shù)應(yīng)用有確保復(fù)制“對(duì)象內(nèi)的所有成員變量”及“所有base class成分”;

不要嘗試以某個(gè)copying函數(shù)調(diào)用另一個(gè)copying函數(shù)。應(yīng)該將共同機(jī)能放進(jìn)第三個(gè)函數(shù)中,并由兩個(gè)copying函數(shù)公用。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

新聞名稱:EffectiveC++(2)----構(gòu)造/析構(gòu)/賦值運(yùn)算-創(chuàng)新互聯(lián)
文章來(lái)源:http://muchs.cn/article6/ippig.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、企業(yè)網(wǎng)站制作、靜態(tài)網(wǎng)站Google、網(wǎng)站內(nèi)鏈微信公眾號(hào)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名