Effective C 讀書筆記3

2021-06-06 14:31:53 字數 3813 閱讀 4348

條款8: 別讓異常逃離析構函式

c++並不禁止析構函式吐出異常,但不鼓勵這樣做。但如果你的析構函式必須執行乙個動作,而該動作可能會在失敗時丟擲異常,該怎麼辦?

兩個辦法解決:

一是 如果丟擲異常程式就結束,通常通過呼叫abort函式完成:

dbconn::~dbconn()catch()

}

如果程式遭遇乙個於析構期間發生的錯誤後無法繼續執行,強迫結束程式是個合理的選擇,因為他可以阻止異常從析構函式傳播出去。

二是 吞下異常:

dbconn::~dbconn()catch()

}

一般而言,將異常吞掉是個壞主意,因為它壓制了某些動作失敗的重要資訊!然而有時候吞下一場也比負擔「草率結束程式」好。

請記住:

1.析構函式絕對不要吐出異常。如果乙個被析構函式呼叫的函式可能丟擲異常,析構函式應該撲捉異常,然後吞下它們(不傳播)或者結束程式。

2.如果客戶需要對某個操作函式執行期間丟擲的異常做出反應,那麼class應該提供乙個普通函式(而非在析構函式中)執行該操作。??????

條款9: 絕不在構造和析構過程中呼叫virtual函式

class transaction;

transaction::transaction()

class buytransaction: public transaction;

class selltransaction: public transaction;

執行以下語句:

buytransaction b;

基類transaction建構函式一定會被更早呼叫,但其物件內的基類成分會在派生類自身成分被構造之前先構造妥當。但基類建構函式的最後一行呼叫virtual函式logtransaction,這時被呼叫的logtransaction是基類內的版本。是的,基類構造期間虛函式不會下降到派生類階層。解決方法是:在基類內將logtransaction函式改為non-virtual,然後要求派生類建構函式傳遞必要資訊給基類建構函式,而後那個建構函式便可以安全的呼叫non-virtual的logtransaction了,像這樣:

class transaction;

transaction::transaction(const std::string& loginfo)

class buytransaction: public transaction

private:

static std::string createlogstring(parameters);

};

由於無法使用virtual函式從基類向下呼叫,在構造期間,可以籍由令派生類將必要資訊向上傳遞至基類建構函式的方法代替。

請記住:在構造和析構期間不要呼叫virtual函式,因為這類呼叫從不下降至派生類

條款10:令operator=返回乙個reference to *this

條款11:在operator=中處理「自我賦值」

自我賦值發生在

物件被賦值給自己時.

如果遵循條款13和14的忠告,你會運用物件來管理資源,而且你可以確定所謂資源管理物件在copy發生時有正確的舉措。這種情況下你的賦值操作符或許是自我賦值安全的,不需要額外操心。然而如果你嘗試自行管理資源,可能會掉進「在停止使用資源之前意外釋放了它」的陷阱。假設你建立乙個類來儲存乙個指標指向一塊動態分配的點陣圖:

class bitmap;

class widget;

下面的operator=實現不安全:

widget& widget::operator=(const widget& rhs)
問題是operator=內的*this和rhs有可能是同一物件,那麼delete就不止delete了當前物件的bitmap,也銷毀了rhs的bitmap。那麼使用rhs的pb拷貝構造出來的新物件就無從談起,返回的*this持有來了乙個已經被銷毀的指標。要阻止這類錯誤,需要在operator=的最前面加上乙個「證同測試」:

widget& widget::operator=(const widget& rhs)

delete pb;

pb = new bitmap(*rhs.pb);

return *this;

}

但這樣不足以使**具有「異常安全性」,因為new 操作可能失敗,這樣widget最終會持有乙個指標指向一塊被delete的bitmap。解決方法是:

widget& widget::operator=(const widget& rhs)

請記住:

2.確定任何函式如果操作乙個以上的物件,而其中多個物件是同乙個物件時,其行為仍然正確。

條款12:複製物件時勿忘其每乙個成分

如果你宣告自己的拷貝函式,意思是告訴編譯器你不喜歡預設實現中的某些行為。編譯器彷彿被冒犯似得,會以一種奇怪的方式回敬:當你的實現**幾乎必然出錯時卻不告訴你:

void logcall(const std::string& funcname);

class customer;

customer::customer(const customer& rhs):name(rhs.name)

customrer::customrer::operator=(const customrer& rhs)

當另乙個新成員變數加入後,如果程式設計師沒有把該新成員變數在拷貝賦值和拷貝構造中處理,大多數編譯器不會警告。發生繼承時,可能會造成此一主題最暗中肆虐的乙個潛在危機:

class prioritycustomer: public customer;

prioritycustomer::prioritycustomer(const prioritycustomer& rhs):priority(rhs.priority)

prioritycustomer& operator=(const prioritycustomer& rhs)

以上兩個實現複製了prioritycustomer宣告的成員變數,但每個prioritycustomer還內含它所繼承的customer成員變數復件,而那些成員變數卻未被複製。所以任何時候只要你承擔起為派生類撰寫拷貝函式的重大責任,必須很小心的也複製其基類成分。那些基類成分往往是private,所以無法直接訪問,應該讓派生類的拷貝函式呼叫積累的拷貝函式:

prioritycustomer::prioritycustomer(const prioritycustomer& rhs):customer(rhs),priority(rhs.priority)

prioritycustomer& operator=(const prioritycustomer& rhs)

請記住:

1.拷貝函式應該確保複製物件內的所有成員變數及所有基類成分。

2.不要嘗試某個拷貝函式實現另乙個拷貝函式,應該將共同技能放進第三個函式中,並油兩個拷貝函式共同呼叫。

effective C 讀書筆記 3

1 p18 頁 經過我的實驗 void f1 const a a 和void f2 a const a 是不同的!莫非書上有錯?2 stl迭代器 天生就是 t const ptr 如果需要乙個 const t ptr 則需要的是 const iterator 3 這個表要記一下子 3 non con...

《effective C 》讀書筆記

1,c 關鍵字explicit c 中,乙個引數的 建構函式 或者除了第乙個引數外其餘引數都有預設值的多參建構函式 承擔了兩個角色。1 是個 構造器,2 是個預設且隱含的型別轉換操作符 所以,有時候在我們寫下如 aaa 這樣的 且恰好 的型別正好是aaa單引數構造器的引數型別,這時候 編譯器就自動呼...

Effective C 讀書筆記

一 讓自己習慣c 1 條款01 視c 為聯邦語言 c 的組成可分為四部分 1.c c 仍然以c語言為基礎。區塊 語句 預處理 內建資料型別 陣列 指標等都來自c。2.object oriented c c with classes所訴說的 classes 包括構造和析構 封裝 繼承 多型 virtu...