首頁(yè)技術(shù)文章正文

Google C++ 編程風(fēng)格指南:類

更新時(shí)間:2018-11-29 來(lái)源:黑馬程序員技術(shù)社區(qū) 瀏覽量:

3.1. 構(gòu)造函數(shù)的職責(zé)

不要在構(gòu)造函數(shù)中進(jìn)行復(fù)雜的初始化 (尤其是那些有可能失敗或者需要調(diào)用虛函數(shù)的初始化).


定義:

在構(gòu)造函數(shù)體中進(jìn)行初始化操作.

優(yōu)點(diǎn):

排版方便, 無(wú)需擔(dān)心類是否已經(jīng)初始化.

缺點(diǎn):

在構(gòu)造函數(shù)中執(zhí)行操作引起的問(wèn)題有:


構(gòu)造函數(shù)中很難上報(bào)錯(cuò)誤, 不能使用異常.操作失敗會(huì)造成對(duì)象初始化失敗,進(jìn)入不確定狀態(tài).如果在構(gòu)造函數(shù)內(nèi)調(diào)用了自身的虛函數(shù), 這類調(diào)用是不會(huì)重定向到子類的虛函數(shù)實(shí)現(xiàn). 即使當(dāng)前沒(méi)有子類化實(shí)現(xiàn), 將來(lái)仍是隱患.如果有人創(chuàng)建該類型的全局變量 (雖然違背了上節(jié)提到的規(guī)則), 構(gòu)造函數(shù)將先 main() 一步被調(diào)用, 有可能破壞構(gòu)造函數(shù)中暗含的假設(shè)條件. 例如, gflags 尚未初始化.

結(jié)論:

構(gòu)造函數(shù)不得調(diào)用虛函數(shù), 或嘗試報(bào)告一個(gè)非致命錯(cuò)誤. 如果對(duì)象需要進(jìn)行有意義的 (non-trivial) 初始化, 考慮使用明確的 Init() 方法或使用工廠模式.


3.2. 初始化

如果類中定義了成員變量, 則必須在類中為每個(gè)類提供初始化函數(shù)或定義一個(gè)構(gòu)造函數(shù). 若未聲明構(gòu)造函數(shù), 則編譯器會(huì)生成一個(gè)默認(rèn)的構(gòu)造函數(shù), 這有可能導(dǎo)致某些成員未被初始化或被初始化為不恰當(dāng)?shù)闹?


定義:

new 一個(gè)不帶參數(shù)的類對(duì)象時(shí), 會(huì)調(diào)用這個(gè)類的默認(rèn)構(gòu)造函數(shù). 用 new[] 創(chuàng)建數(shù)組時(shí), 默認(rèn)構(gòu)造函數(shù)則總是被調(diào)用. 在類成員里面進(jìn)行初始化是指聲明一個(gè)成員變量的時(shí)候使用一個(gè)結(jié)構(gòu)例如 int _count = 17 或者 string _name{"abc"} 來(lái)替代 int _count 或者 string _name 這樣的形式.

優(yōu)點(diǎn):

用戶定義的默認(rèn)構(gòu)造函數(shù)將在沒(méi)有提供初始化操作時(shí)將對(duì)象初始化. 這樣就保證了對(duì)象在被構(gòu)造之時(shí)就處于一個(gè)有效且可用的狀態(tài), 同時(shí)保證了對(duì)象在被創(chuàng)建時(shí)就處于一個(gè)顯然”不可能”的狀態(tài), 以此幫助調(diào)試.

缺點(diǎn):

對(duì)代碼編寫(xiě)者來(lái)說(shuō), 這是多余的工作.

如果一個(gè)成員變量在聲明時(shí)初始化又在構(gòu)造函數(shù)中初始化, 有可能造成混亂, 因?yàn)闃?gòu)造函數(shù)中的值會(huì)覆蓋掉聲明中的值.

結(jié)論:

簡(jiǎn)單的初始化用類成員初始化完成, 尤其是當(dāng)一個(gè)成員變量要在多個(gè)構(gòu)造函數(shù)里用相同的方式初始化的時(shí)候.

如果你的類中有成員變量沒(méi)有在類里面進(jìn)行初始化, 而且沒(méi)有提供其它構(gòu)造函數(shù), 你必須定義一個(gè) (不帶參數(shù)的) 默認(rèn)構(gòu)造函數(shù). 把對(duì)象的內(nèi)部狀態(tài)初始化成一致 / 有效的值無(wú)疑是更合理的方式.


這么做的原因是: 如果你沒(méi)有提供其它構(gòu)造函數(shù), 又沒(méi)有定義默認(rèn)構(gòu)造函數(shù), 編譯器將為你自動(dòng)生成一個(gè). 編譯器生成的構(gòu)造函數(shù)并不會(huì)對(duì)對(duì)象進(jìn)行合理的初始化.


如果你定義的類繼承現(xiàn)有類, 而你又沒(méi)有增加新的成員變量, 則不需要為新類定義默認(rèn)構(gòu)造函數(shù).



3.3. 顯式構(gòu)造函數(shù)

對(duì)單個(gè)參數(shù)的構(gòu)造函數(shù)使用 C++ 關(guān)鍵字 explicit.


定義:

通常, 如果構(gòu)造函數(shù)只有一個(gè)參數(shù), 可看成是一種隱式轉(zhuǎn)換. 打個(gè)比方, 如果你定義了 Foo::Foo(string name), 接著把一個(gè)字符串傳給一個(gè)以 Foo 對(duì)象為參數(shù)的函數(shù), 構(gòu)造函數(shù) Foo::Foo(string name) 將被調(diào)用, 并將該字符串轉(zhuǎn)換為一個(gè) Foo 的臨時(shí)對(duì)象傳給調(diào)用函數(shù). 看上去很方便, 但如果你并不希望如此通過(guò)轉(zhuǎn)換生成一個(gè)新對(duì)象的話, 麻煩也隨之而來(lái). 為避免構(gòu)造函數(shù)被調(diào)用造成隱式轉(zhuǎn)換, 可以將其聲明為 explicit.

除單參數(shù)構(gòu)造函數(shù)外, 這一規(guī)則也適用于除第一個(gè)參數(shù)以外的其他參數(shù)都具有默認(rèn)參數(shù)的構(gòu)造函數(shù), 例如 Foo::Foo(string name, int id = 42).

優(yōu)點(diǎn):

避免不合時(shí)宜的變換.

缺點(diǎn):

無(wú)

結(jié)論:

所有單參數(shù)構(gòu)造函數(shù)都必須是顯式的. 在類定義中, 將關(guān)鍵字 explicit 加到單參數(shù)構(gòu)造函數(shù)前: explicit Foo(string name);


例外: 在極少數(shù)情況下, 拷貝構(gòu)造函數(shù)可以不聲明成 explicit. 作為其它類的透明包裝器的類也是特例之一. 類似的例外情況應(yīng)在注釋中明確說(shuō)明.


最后, 只有 std::initializer_list 的構(gòu)造函數(shù)可以是非 explicit, 以允許你的類型結(jié)構(gòu)可以使用列表初始化的方式進(jìn)行賦值. 例如:


MyType m = {1, 2};MyType MakeMyType() { return {1, 2}; }TakeMyType({1, 2});

3.4. 可拷貝類型和可移動(dòng)類型

如果你的類型需要, 就讓它們支持拷貝 / 移動(dòng). 否則, 就把隱式產(chǎn)生的拷貝和移動(dòng)函數(shù)禁用.


定義:

可拷貝類型允許對(duì)象在初始化時(shí)得到來(lái)自相同類型的另一對(duì)象的值, 或在賦值時(shí)被賦予相同類型的另一對(duì)象的值, 同時(shí)不改變?cè)磳?duì)象的值. 對(duì)于用戶定義的類型, 拷貝操作一般通過(guò)拷貝構(gòu)造函數(shù)與拷貝賦值操作符定義. string 類型就是一個(gè)可拷貝類型的例子.


可移動(dòng)類型允許對(duì)象在初始化時(shí)得到來(lái)自相同類型的臨時(shí)對(duì)象的值, 或在賦值時(shí)被賦予相同類型的臨時(shí)對(duì)象的值 (因此所有可拷貝對(duì)象也是可移動(dòng)的). std::unique_ptr<int> 就是一個(gè)可移動(dòng)但不可復(fù)制的對(duì)象的例子. 對(duì)于用戶定義的類型, 移動(dòng)操作一般是通過(guò)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符實(shí)現(xiàn)的.


拷貝 / 移動(dòng)構(gòu)造函數(shù)在某些情況下會(huì)被編譯器隱式調(diào)用. 例如, 通過(guò)傳值的方式傳遞對(duì)象.


優(yōu)點(diǎn):

可移動(dòng)及可拷貝類型的對(duì)象可以通過(guò)傳值的方式進(jìn)行傳遞或者返回, 這使得 API 更簡(jiǎn)單, 更安全也更通用. 與傳指針和引用不同, 這樣的傳遞不會(huì)造成所有權(quán), 生命周期, 可變性等方面的混亂, 也就沒(méi)必要在協(xié)議中予以明確. 這同時(shí)也防止了客戶端與實(shí)現(xiàn)在非作用域內(nèi)的交互, 使得它們更容易被理解與維護(hù). 這樣的對(duì)象可以和需要傳值操作的通用 API 一起使用, 例如大多數(shù)容器.


拷貝 / 移動(dòng)構(gòu)造函數(shù)與賦值操作一般來(lái)說(shuō)要比它們的各種替代方案, 比如 Clone(), CopyFrom() or Swap(), 更容易定義, 因?yàn)樗鼈兡芡ㄟ^(guò)編譯器產(chǎn)生, 無(wú)論是隱式的還是通過(guò) = 默認(rèn). 這種方式很簡(jiǎn)潔, 也保證所有數(shù)據(jù)成員都會(huì)被復(fù)制. 拷貝與移動(dòng)構(gòu)造函數(shù)一般也更高效, 因?yàn)樗鼈儾恍枰训姆峙浠蛘呤菃为?dú)的初始化和賦值步驟, 同時(shí), 對(duì)于類似省略不必要的拷貝這樣的優(yōu)化它們也更加合適.


移動(dòng)操作允許隱式且高效地將源數(shù)據(jù)轉(zhuǎn)移出右值對(duì)象. 這有時(shí)能讓代碼風(fēng)格更加清晰.


缺點(diǎn):

許多類型都不需要拷貝, 為它們提供拷貝操作會(huì)讓人迷惑, 也顯得荒謬而不合理. 為基類提供拷貝 / 賦值操作是有害的, 因?yàn)樵谑褂盟鼈儠r(shí)會(huì)造成對(duì)象切割. 默認(rèn)的或者隨意的拷貝操作實(shí)現(xiàn)可能是不正確的, 這往往導(dǎo)致令人困惑并且難以診斷出的錯(cuò)誤.


拷貝構(gòu)造函數(shù)是隱式調(diào)用的, 也就是說(shuō), 這些調(diào)用很容易被忽略. 這會(huì)讓人迷惑, 尤其是對(duì)那些所用的語(yǔ)言約定或強(qiáng)制要求傳引用的程序員來(lái)說(shuō)更是如此. 同時(shí), 這從一定程度上說(shuō)會(huì)鼓勵(lì)過(guò)度拷貝, 從而導(dǎo)致性能上的問(wèn)題.

結(jié)論:

如果需要就讓你的類型可拷貝 / 可移動(dòng). 作為一個(gè)經(jīng)驗(yàn)法則, 如果對(duì)于你的用戶來(lái)說(shuō)這個(gè)拷貝操作不是一眼就能看出來(lái)的, 那就不要把類型設(shè)置為可拷貝. 如果讓類型可拷貝, 一定要同時(shí)給出拷貝構(gòu)造函數(shù)和賦值操作的定義. 如果讓類型可拷貝, 同時(shí)移動(dòng)操作的效率高于拷貝操作, 那么就把移動(dòng)的兩個(gè)操作 (移動(dòng)構(gòu)造函數(shù)和賦值操作) 也給出定義. 如果類型不可拷貝, 但是移動(dòng)操作的正確性對(duì)用戶顯然可見(jiàn), 那么把這個(gè)類型設(shè)置為只可移動(dòng)并定義移動(dòng)的兩個(gè)操作.


建議通過(guò) = default 定義拷貝和移動(dòng)操作. 定義非默認(rèn)的移動(dòng)操作目前需要異常. 時(shí)刻記得檢測(cè)默認(rèn)操作的正確性. 由于存在對(duì)象切割的風(fēng)險(xiǎn), 不要為任何有可能有派生類的對(duì)象提供賦值操作或者拷貝 / 移動(dòng)構(gòu)造函數(shù) (當(dāng)然也不要繼承有這樣的成員函數(shù)的類). 如果你的基類需要可復(fù)制屬性, 請(qǐng)?zhí)峁┮粋€(gè) public virtual Clone() 和一個(gè) protected 的拷貝構(gòu)造函數(shù)以供派生類實(shí)現(xiàn).


如果你的類不需要拷貝 / 移動(dòng)操作, 請(qǐng)顯式地通過(guò) = delete 或其他手段禁用之.



3.5. 委派和繼承構(gòu)造函數(shù)

在能夠減少重復(fù)代碼的情況下使用委派和繼承構(gòu)造函數(shù).


定義:

委派和繼承構(gòu)造函數(shù)是由 C++11 引進(jìn)為了減少構(gòu)造函數(shù)重復(fù)代碼而開(kāi)發(fā)的兩種不同的特性. 通過(guò)特殊的初始化列表語(yǔ)法, 委派構(gòu)造函數(shù)允許類的一個(gè)構(gòu)造函數(shù)調(diào)用其他的構(gòu)造函數(shù). 例如:


X::X(const string& name) : name_(name) {  ...}X::X() : X("") { }

繼承構(gòu)造函數(shù)允許派生類直接調(diào)用基類的構(gòu)造函數(shù), 一如繼承基類的其他成員函數(shù), 而無(wú)需重新聲明. 當(dāng)基類擁有多個(gè)構(gòu)造函數(shù)時(shí)這一功能尤其有用. 例如:


class Base { public:  Base();  Base(int n);  Base(const string& s);  ...};class Derived : public Base { public:  using Base::Base;  // Base's constructors are redeclared here.};

如果派生類的構(gòu)造函數(shù)只是調(diào)用基類的構(gòu)造函數(shù)而沒(méi)有其他行為時(shí), 這一功能特別有用.

優(yōu)點(diǎn):

委派和繼承構(gòu)造函數(shù)可以減少冗余代碼, 提高可讀性. 委派構(gòu)造函數(shù)對(duì) Java 程序員來(lái)說(shuō)并不陌生.

缺點(diǎn):

使用輔助函數(shù)可以預(yù)估出委派構(gòu)造函數(shù)的行為. 如果派生類和基類相比引入了新的成員變量, 繼承構(gòu)造函數(shù)就會(huì)讓人迷惑, 因?yàn)榛惒⒉恢肋@些新的成員變量的存在.

結(jié)論:

只在能夠減少冗余代碼, 提高可讀性的前提下使用委派和繼承構(gòu)造函數(shù). 如果派生類有新的成員變量, 那么使用繼承構(gòu)造函數(shù)時(shí)要小心. 如果在派生類中對(duì)成員變量使用了類內(nèi)部初始化的話, 繼承構(gòu)造函數(shù)還是適用的.


3.6. 結(jié)構(gòu)體 VS. 類

僅當(dāng)只有數(shù)據(jù)時(shí)使用 struct, 其它一概使用 class.


說(shuō)明:

在 C++ 中 struct 和 class 關(guān)鍵字幾乎含義一樣. 我們?yōu)檫@兩個(gè)關(guān)鍵字添加我們自己的語(yǔ)義理解, 以便未定義的數(shù)據(jù)類型選擇合適的關(guān)鍵字.

struct 用來(lái)定義包含數(shù)據(jù)的被動(dòng)式對(duì)象, 也可以包含相關(guān)的常量, 但除了存取數(shù)據(jù)成員之外, 沒(méi)有別的函數(shù)功能. 并且存取功能是通過(guò)直接訪問(wèn)位域, 而非函數(shù)調(diào)用. 除了構(gòu)造函數(shù), 析構(gòu)函數(shù), Initialize(), Reset(), Validate() 等類似的函數(shù)外, 不能提供其它功能的函數(shù).

如果需要更多的函數(shù)功能, class 更適合. 如果拿不準(zhǔn), 就用 class.

為了和 STL 保持一致, 對(duì)于仿函數(shù)和 trait 特性可以不用 class 而是使用 struct.

注意: 類和結(jié)構(gòu)體的成員變量使用不同的命名規(guī)則.


3.7. 繼承

使用組合 (composition, YuleFox 注: 這一點(diǎn)也是 GoF 在 <<Design Patterns>> 里反復(fù)強(qiáng)調(diào)的) 常常比使用繼承更合理. 如果使用繼承的話, 定義為 public 繼承.


定義:

當(dāng)子類繼承基類時(shí), 子類包含了父基類所有數(shù)據(jù)及操作的定義. C++ 實(shí)踐中, 繼承主要用于兩種場(chǎng)合: 實(shí)現(xiàn)繼承 (implementation inheritance), 子類繼承父類的實(shí)現(xiàn)代碼; 接口繼承 (interface inheritance), 子類僅繼承父類的方法名稱.

優(yōu)點(diǎn):

實(shí)現(xiàn)繼承通過(guò)原封不動(dòng)的復(fù)用基類代碼減少了代碼量. 由于繼承是在編譯時(shí)聲明, 程序員和編譯器都可以理解相應(yīng)操作并發(fā)現(xiàn)錯(cuò)誤. 從編程角度而言, 接口繼承是用來(lái)強(qiáng)制類輸出特定的 API. 在類沒(méi)有實(shí)現(xiàn) API 中某個(gè)必須的方法時(shí), 編譯器同樣會(huì)發(fā)現(xiàn)并報(bào)告錯(cuò)誤.

缺點(diǎn):

對(duì)于實(shí)現(xiàn)繼承, 由于子類的實(shí)現(xiàn)代碼散布在父類和子類間之間, 要理解其實(shí)現(xiàn)變得更加困難. 子類不能重寫(xiě)父類的非虛函數(shù), 當(dāng)然也就不能修改其實(shí)現(xiàn). 基類也可能定義了一些數(shù)據(jù)成員, 還要區(qū)分基類的實(shí)際布局.

結(jié)論:

所有繼承必須是 public 的. 如果你想使用私有繼承, 你應(yīng)該替換成把基類的實(shí)例作為成員對(duì)象的方式.

不要過(guò)度使用實(shí)現(xiàn)繼承. 組合常常更合適一些. 盡量做到只在 “是一個(gè)” (“is-a”, YuleFox 注: 其他 “has-a” 情況下請(qǐng)使用組合) 的情況下使用繼承: 如果 Bar 的確 “是一種” Foo, Bar 才能繼承 Foo.

必要的話, 析構(gòu)函數(shù)聲明為 virtual. 如果你的類有虛函數(shù), 則析構(gòu)函數(shù)也應(yīng)該為虛函數(shù). 注意 數(shù)據(jù)成員在任何情況下都必須是私有的.

當(dāng)重載一個(gè)虛函數(shù), 在衍生類中把它明確的聲明為 virtual. 理論依據(jù): 如果省略 virtual 關(guān)鍵字, 代碼閱讀者不得不檢查所有父類, 以判斷該函數(shù)是否是虛函數(shù).


3.8. 多重繼承

真正需要用到多重實(shí)現(xiàn)繼承的情況少之又少. 只在以下情況我們才允許多重繼承: 最多只有一個(gè)基類是非抽象類; 其它基類都是以 Interface 為后綴的 純接口類.


定義:

多重繼承允許子類擁有多個(gè)基類. 要將作為 純接口 的基類和具有 實(shí)現(xiàn) 的基類區(qū)別開(kāi)來(lái).

優(yōu)點(diǎn):

相比單繼承 (見(jiàn) 繼承), 多重實(shí)現(xiàn)繼承可以復(fù)用更多的代碼.

缺點(diǎn):

真正需要用到多重 實(shí)現(xiàn) 繼承的情況少之又少. 多重實(shí)現(xiàn)繼承看上去是不錯(cuò)的解決方案, 但你通常也可以找到一個(gè)更明確, 更清晰的不同解決方案.

結(jié)論:

只有當(dāng)所有父類除第一個(gè)外都是 純接口類 時(shí), 才允許使用多重繼承. 為確保它們是純接口, 這些類必須以 Interface 為后綴.

關(guān)于該規(guī)則, Windows 下有個(gè) 特例.



3.9. 接口

接口是指滿足特定條件的類, 這些類以 Interface 為后綴 (不強(qiáng)制).


定義:

當(dāng)一個(gè)類滿足以下要求時(shí), 稱之為純接口:


只有純虛函數(shù) (“=0”) 和靜態(tài)函數(shù) (除了下文提到的析構(gòu)函數(shù)).沒(méi)有非靜態(tài)數(shù)據(jù)成員.沒(méi)有定義任何構(gòu)造函數(shù). 如果有, 也不能帶有參數(shù), 并且必須為 protected.如果它是一個(gè)子類, 也只能從滿足上述條件并以 Interface 為后綴的類繼承.

接口類不能被直接實(shí)例化, 因?yàn)樗暶髁思兲摵瘮?shù). 為確保接口類的所有實(shí)現(xiàn)可被正確銷毀, 必須為之聲明虛析構(gòu)函數(shù) (作為上述第 1 條規(guī)則的特例, 析構(gòu)函數(shù)不能是純虛函數(shù)). 具體細(xì)節(jié)可參考 Stroustrup 的 The C++ Programming Language, 3rd edition 第 12.4 節(jié).

優(yōu)點(diǎn):

以 Interface 為后綴可以提醒其他人不要為該接口類增加函數(shù)實(shí)現(xiàn)或非靜態(tài)數(shù)據(jù)成員. 這一點(diǎn)對(duì)于 多重繼承 尤其重要. 另外, 對(duì)于 Java 程序員來(lái)說(shuō), 接口的概念已是深入人心.

缺點(diǎn):

Interface 后綴增加了類名長(zhǎng)度, 為閱讀和理解帶來(lái)不便. 同時(shí),接口特性作為實(shí)現(xiàn)細(xì)節(jié)不應(yīng)暴露給用戶.

結(jié)論:

只有在滿足上述需要時(shí), 類才以 Interface 結(jié)尾, 但反過(guò)來(lái), 滿足上述需要的類未必一定以 Interface 結(jié)尾.


3.10. 運(yùn)算符重載

除少數(shù)特定環(huán)境外,不要重載運(yùn)算符.


定義:

一個(gè)類可以定義諸如 + 和 / 等運(yùn)算符, 使其可以像內(nèi)建類型一樣直接操作.

優(yōu)點(diǎn):

使代碼看上去更加直觀, 類表現(xiàn)的和內(nèi)建類型 (如 int) 行為一致. 重載運(yùn)算符使 Equals(), Add()等函數(shù)名黯然失色. 為了使一些模板函數(shù)正確工作, 你可能必須定義操作符.

缺點(diǎn):

雖然操作符重載令代碼更加直觀, 但也有一些不足:

混淆視聽(tīng), 讓你誤以為一些耗時(shí)的操作和操作內(nèi)建類型一樣輕巧.更難定位重載運(yùn)算符的調(diào)用點(diǎn), 查找 Equals() 顯然比對(duì)應(yīng)的 == 調(diào)用點(diǎn)要容易的多.有的運(yùn)算符可以對(duì)指針進(jìn)行操作, 容易導(dǎo)致 bug. Foo + 4 做的是一件事, 而 &Foo + 4 可能做的是完全不同的另一件事. 對(duì)于二者, 編譯器都不會(huì)報(bào)錯(cuò), 使其很難調(diào)試;

重載還有令你吃驚的副作用. 比如, 重載了 operator& 的類不能被前置聲明.

結(jié)論:

一般不要重載運(yùn)算符. 尤其是賦值操作 (operator=) 比較詭異, 應(yīng)避免重載. 如果需要的話, 可以定義類似 Equals(), CopyFrom() 等函數(shù).

然而, 極少數(shù)情況下可能需要重載運(yùn)算符以便與模板或 “標(biāo)準(zhǔn)” C++ 類互操作 (如 operator<<(ostream&, const T&)). 只有被證明是完全合理的才能重載, 但你還是要盡可能避免這樣做. 尤其是不要僅僅為了在 STL 容器中用作鍵值就重載 operator== 或 operator<; 相反, 你應(yīng)該在聲明容器的時(shí)候, 創(chuàng)建相等判斷和大小比較的仿函數(shù)類型.

有些 STL 算法確實(shí)需要重載 operator== 時(shí), 你可以這么做, 記得別忘了在文檔中說(shuō)明原因.

參考 拷貝構(gòu)造函數(shù) 和 函數(shù)重載.


3.11. 存取控制

將 所有 數(shù)據(jù)成員聲明為 private, 并根據(jù)需要提供相應(yīng)的存取函數(shù). 例如, 某個(gè)名為 foo_ 的變量, 其取值函數(shù)是 foo(). 還可能需要一個(gè)賦值函數(shù) set_foo().

特例是, 靜態(tài)常量數(shù)據(jù)成員 (一般寫(xiě)做 kFoo) 不需要是私有成員.

一般在頭文件中把存取函數(shù)定義成內(nèi)聯(lián)函數(shù).

參考 繼承 和 函數(shù)命名



3.11. 聲明順序

在類中使用特定的聲明順序: public: 在 private: 之前, 成員函數(shù)在數(shù)據(jù)成員 (變量) 前;


類的訪問(wèn)控制區(qū)段的聲明順序依次為: public:, protected:, private:. 如果某區(qū)段沒(méi)內(nèi)容, 可以不聲明.

每個(gè)區(qū)段內(nèi)的聲明通常按以下順序:

typedefs 和枚舉常量構(gòu)造函數(shù)析構(gòu)函數(shù)成員函數(shù), 含靜態(tài)成員函數(shù)數(shù)據(jù)成員, 含靜態(tài)數(shù)據(jù)成員

友元聲明應(yīng)該放在 private 區(qū)段. 如果用宏 DISALLOW_COPY_AND_ASSIGN 禁用拷貝和賦值, 應(yīng)當(dāng)將其置于 private 區(qū)段的末尾, 也即整個(gè)類聲明的末尾. 參見(jiàn)可拷貝類型和可移動(dòng)類型.

.cc 文件中函數(shù)的定義應(yīng)盡可能和聲明順序一致.

不要在類定義中內(nèi)聯(lián)大型函數(shù). 通常, 只有那些沒(méi)有特別意義或性能要求高, 并且是比較短小的函數(shù)才能被定義為內(nèi)聯(lián)函數(shù). 更多細(xì)節(jié)參考 內(nèi)聯(lián)函數(shù).


3.12. 編寫(xiě)簡(jiǎn)短函數(shù)

傾向編寫(xiě)簡(jiǎn)短, 凝練的函數(shù).


我們承認(rèn)長(zhǎng)函數(shù)有時(shí)是合理的, 因此并不硬性限制函數(shù)的長(zhǎng)度. 如果函數(shù)超過(guò) 40 行, 可以思索一下能不能在不影響程序結(jié)構(gòu)的前提下對(duì)其進(jìn)行分割.

即使一個(gè)長(zhǎng)函數(shù)現(xiàn)在工作的非常好, 一旦有人對(duì)其修改, 有可能出現(xiàn)新的問(wèn)題. 甚至導(dǎo)致難以發(fā)現(xiàn)的 bug. 使函數(shù)盡量簡(jiǎn)短, 便于他人閱讀和修改代碼.

在處理代碼時(shí), 你可能會(huì)發(fā)現(xiàn)復(fù)雜的長(zhǎng)函數(shù). 不要害怕修改現(xiàn)有代碼: 如果證實(shí)這些代碼使用 / 調(diào)試?yán)щy, 或者你需要使用其中的一小段代碼, 考慮將其分割為更加簡(jiǎn)短并易于管理的若干函數(shù).


譯者 (YuleFox) 筆記不在構(gòu)造函數(shù)中做太多邏輯相關(guān)的初始化;編譯器提供的默認(rèn)構(gòu)造函數(shù)不會(huì)對(duì)變量進(jìn)行初始化, 如果定義了其他構(gòu)造函數(shù), 編譯器不再提供, 需要編碼者自行提供默認(rèn)構(gòu)造函數(shù);為避免隱式轉(zhuǎn)換, 需將單參數(shù)構(gòu)造函數(shù)聲明為 explicit;為避免拷貝構(gòu)造函數(shù), 賦值操作的濫用和編譯器自動(dòng)生成, 可將其聲明為 private 且無(wú)需實(shí)現(xiàn);僅在作為數(shù)據(jù)集合時(shí)使用 struct;組合 > 實(shí)現(xiàn)繼承 > 接口繼承 > 私有繼承, 子類重載的虛函數(shù)也要聲明 virtual 關(guān)鍵字, 雖然編譯器允許不這樣做;避免使用多重繼承, 使用時(shí), 除一個(gè)基類含有實(shí)現(xiàn)外, 其他基類均為純接口;接口類類名以 Interface 為后綴, 除提供帶實(shí)現(xiàn)的虛析構(gòu)函數(shù), 靜態(tài)成員函數(shù)外, 其他均為純虛函數(shù), 不定義非靜態(tài)數(shù)據(jù)成員, 不提供構(gòu)造函數(shù), 提供的話,聲明為 protected;為降低復(fù)雜性, 盡量不重載操作符, 模板, 標(biāo)準(zhǔn)類中使用時(shí)提供文檔說(shuō)明;存取函數(shù)一般內(nèi)聯(lián)在頭文件中;聲明次序: public -> protected -> private;函數(shù)體盡量短小, 緊湊, 功能單一;    

   
作者:黑馬程序員C/C++全棧培訓(xùn)學(xué)院

首發(fā): http://java.itheima.com

分享到:
在線咨詢 我要報(bào)名
和我們?cè)诰€交談!