讀書筆記 1

2021-07-03 07:20:13 字數 4955 閱讀 5303

條款01:視c++為乙個語言聯邦

條款02:盡量以const,enum,inline替換#define

條款03:盡可能使用const

條款04:確定物件被使用前已先被初始化

條款05:了解c++默默編寫並呼叫哪些函式

什麼時候empty class不再是個empty class呢?當c++處理過它之後.是的,如果你自己沒有宣告,編譯器就會為它宣告乙個copy建構函式,乙個copy assignment操作符和乙個析構函式.此外如果你沒有宣告任何建構函式,編譯器也會為你宣告乙個default建構函式.所有這些函式都是public且inline.因此,如果你寫下:

class empty{};

這就好像你寫下這樣的**:

class empty

empty(const empty & rhs)

~empty()

empty & operator=(const empty & rhs)

};惟有當這些函式被需要,它們才會被編譯器建立出來.程式中需要它們是很平常的事.下面**造成上述每乙個函式被編譯器產生:

empty el;

//default建構函式

empty e2(e1);

//copy建構函式

e2 = e1;

//copy assignment操作符

條款06:若不想使用編譯器自動生成的函式,就該明確拒絕

class homeforsale;

讓homeforsale物件拷貝動作以失敗收場:

homeforsale h1;

homeforsale h2;

homeforsale h3(h1);

//企圖拷貝h1--不該通過編譯

h1 = h2;

//企圖拷貝h2--也不該通過編譯

通常如果你不希望class支援某一特定機能,只要不宣告對應函式就是了.但這個策略對copy建構函式和copy assignment操作符不起作用,條款05已經指出,如果你不宣告它們,而某些人嘗試呼叫它們,編譯器會為你宣告它們.

如果你不宣告copy建構函式或copy assignment操作符,編譯器為你產出乙份,如果你宣告它們,你的class還是支援copying.但這裡的目標卻是要阻止copying.

答案的關鍵是,所有編譯器產出的函式都是public.為阻止這些函式被建立出來,你得自行宣告它們,但這裡並沒有什麼需求使你必須將它們宣告為public.因此你可以將copy建構函式或copy assignment操作符宣告為private.藉由明確宣告乙個成員函式,你阻止了編譯器暗自建立其專屬版本;而令這些函式為private,使你得以成功阻止人們呼叫它.

一般而言這個做法並不絕對安全,因為member函式和friend函式還是可以呼叫你的private函式.除非你夠聰明,不去定義它們,那麼如果某些人不慎呼叫任何乙個,會獲得乙個連線錯誤."將成員函式宣告為private而且故意不實現它們"這一伎倆是如此為大家接受,因而被用在c++ iostream程式庫中阻止copying行為.將這個伎倆施行於homeforsale也很簡單:

class homeforsale;

有了上述class定義,當客戶企圖拷貝homeforsale物件,編譯器會阻撓他.如果你不慎在member函式或friend函式之內那麼做,輪到聯結器發出抱怨.

將連線期錯誤移到編譯期是可能的(而且那是好事,畢竟愈早偵測出錯誤愈好),只要將copy建構函式和copy assignamet操作符宣告為private就可以辦到,但不是在homeforsale自身,而是在乙個專門為了阻止copy動作而設計的base class內:

class uncopyable

~uncopyable(){}

private:

uncopyable(const uncopyable &);

uncopyable & coperator=(const uncopyable &);

};class homeforsale:private uncopyable;

現在只要任何人(甚至是member函式或friend函式)嘗試拷貝homeforsale物件,編譯器便試著生成乙個copy建構函式和乙個copy assignment操作符,而正如條款12所說,這些函式的"編譯器生成版"會嘗試呼叫其base class的對應兄弟,那些呼叫會被編譯器拒絕,因為其base class的拷貝函式是private.

條款07:為多型基類宣告virtual析構函式

class timekeeper;

class atomickclock:public timekeeper;

//原子鐘

class waterclock:public timekeeper;

//水鐘

class wristwatch:public timekeeper;

//腕表

設計factory函式,返回指標指向乙個計時物件:

timekeeper * gettimekeeper();

為遵守factory函式的規矩,被gettimekeeper()返回的物件必須位於heap.因此為了避免洩漏記憶體和其他資源,將factory函式返回的每乙個物件適當地delete掉很重要:

timekeeper * ptk = gettimekeeper();

...delete ptk;

問題來了,如果gettimekeeper返回的指標指向乙個derived class物件,而那個物件卻經由乙個base class指標被刪除,而目前的base class(timekeeper)有個non-virtual析構函式.這時很可能derived class成分沒被銷毀,其析構函式也未能執行,而base class成分已被銷毀,於是造成乙個詭異的"區域性銷毀"物件.

消除這個問題的做法很簡單:給base class乙個virtual析構函式.

class timekeeper;

timekeeper * ptk = gettimekeeper();

...delete ptk;

//現在,行為正確

如果class不含virtual函式,通常表示它並不意圖被用作乙個base class.當class不企圖被當作base class,令其析構函式為virtual往往是個餿主意.

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

c++並不禁止析構函式吐出異常,但它不鼓勵你這樣做.考慮以下**:

class widget

//假設這個可能吐出乙個異常

};void dosomething()

//v在這裡被自動銷毀

當vector v被銷毀,它有責任銷毀其內含的所有的widgets.假設v內含十個widgets,而在析構函式第乙個元素期間,有個異常被丟擲.其他九個widgets還是應該被銷毀,因此v應該呼叫它們各個析構函式.但假設在那些呼叫期間,第二個widget析構函式又丟擲異常.現在有兩個同時作用的異常,這對c++而言太多了.在兩個異常同時存在的情況下,程式若不是結束執行就是導致不明確行為.

舉個例子,假設你使用乙個class負責資料庫連線:

class dbconnection;

為確保客戶不忘記在dbconnection物件身上呼叫close(),乙個合理的想法是建立乙個用來管理dbconnection資源的class,並在其析構函式中呼叫close.

class dbconn

private:

dbconnection db;

};這便允許客戶寫出這樣的**:

//開啟乙個區塊(block)

//建立dbconnection物件並交給dbconn物件以便管理

//通過dbconn介面使用dbconnection物件

//在區塊結束點,dbconn物件被銷毀,因而自動為dbconnection物件呼叫close

只要呼叫close成功,一切者美好.但如果該呼叫導致異常,dbconn析構函式會傳播該異常,也就是允許它離開這個析構函式.

兩個辦法可以避免這一問題.dbconn的析構函式可以:

1 如果close丟擲異常就結束程式

dbconn::~dbconn()catch(...)

}2 吞下因呼叫close而發生的異常

dbconn::~dbconn()catch(...)

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

class transaction;

transaction::transaction()

class buytransaction:public transaction;

class selltransaction:public transaction;

當以下行被執行時,會發生什麼事:

buytransaction b;

無疑地會有乙個buytransaction建構函式被呼叫,但首先transaction建構函式一定會更早被呼叫.是的,derived class物件內的base class成分會在derived class自身成分被構造之前先構造妥當.transaction建構函式最後一行呼叫virtual函式logtransaction,這時被呼叫的logtransaction是transaction內的版本,不是buytransaction內的版本--即使目前即將建立的物件型別是buytransaction.

要解決這個問題,一種做法是在class transaction內將logtransaction函式改為non-virtual,然後要求derived class建構函式傳遞必要資訊給transation建構函式:

class transaction;

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

class buytransaction:public transaction

...private:

static std::string createlogstring(parameters);

};換句話說由於你無法使用virtual函式從base classes向下呼叫,在構造期間,你可以藉由"令derived classes將必要的構造資訊向上傳遞至base class建構函式"替換之而加以彌補.

讀書筆記 1

從我第一次看到windows就對它那花花綠綠的外表所吸引,大學兩年過來,時間又讓我從另乙個角度重新認識的了這些美麗的。本學期開始圖形程式設計的學習,探索windows圖形系統,並對gdi api,directdraw api進行學習。之所以寫部落格,第 一 是想勉勵自己不斷學習,讓大家監督 第 二 ...

讀書筆記1

netstat p525 網路資訊服務是通過本地查詢,還是要連線到遠端資料庫 p527 網路配置檔案?服務資訊函式?linux下如何組網 p527 如何設定計算機的主機名 p528 套接字 第15章 套接字 套接字 p513,523 what 套接字 一種程序間通訊機制 不僅可以本地程序通訊,也可以...

讀書筆記1

盡量以const,enum,inline,替換 define 我們無法使用 define建立乙個class專屬常量。因為 define不注重作用域。也不能提供任何的封裝性,也就是說沒有所謂的private define這樣的東西。而const成員是可以被封裝的。乙個const的位址是合法的,但取乙個...