《Effective C++ 中文版 第三版》讀書筆記
這幾乎和前一個設(shè)計一模一樣,只不過 pure virtual 函數(shù) Airplane::fly 替換了獨立函數(shù) Airplane::defaultFly。其聲明部分表現(xiàn)表的是接口(derived class 必須使用的),其定義部分表現(xiàn)出缺省行為(那是 derived class 可能使用的,但只有在它們明確提出申請時才是)。合并 fly 和 defaultFly,就喪失了 “讓兩個函數(shù)享有不同保護級別” 的機會:習慣上被設(shè)為 protected 的函數(shù)(defaultFly)如今成了 public(因為它在 fly 之中)。
如果成員函數(shù)是個 non-virtual 函數(shù),意味著它并不打算在 derived classes 中有不同的行為。non-virtual 成員函數(shù)所表現(xiàn)的不變性凌駕其特異性,無論 derived class 變得多么特異化,它的行為都不可以改變。
聲明 non-virtual 函數(shù)的目的是為了令 derived class 繼承函數(shù)的接口及一份強制性實現(xiàn)。
來看 Shape::objectID 的聲明:可以想做是 “每個 Shape 對象都有一個用來產(chǎn)生對象識別碼的函數(shù):此識別碼總是采用相同計算方法,該方法由 Shape::objectID 的定義式?jīng)Q定,任何 derived class 都不應(yīng)該嘗試改變其行為”。
pure virtual 函數(shù)、simple(impure) virtual 函數(shù)、non-virtual 函數(shù)之間的差異,使你得以精確指定你想要 derived class 繼承的東西:只繼承接口,或是繼承接口和一份缺省實現(xiàn),或是繼承接口和一份強制實現(xiàn)。由于這些不同的類型的聲明意味著根本意義并不相同的事情,當你聲明你的成員函數(shù)時,必須謹慎選擇。如果你確實履行,應(yīng)該能夠避免經(jīng)驗不足的 class 設(shè)計者最常犯的兩個錯誤。
第一個錯誤是將所有的函數(shù)聲明為 non-virtual。這使得 derived class 沒有余裕的空間進行特化工作。non-virtual 析構(gòu)函數(shù)尤其會帶來問題。當然啦,設(shè)計一個并不相乘為基類的 class 是絕對合理的,既然這樣,將其所有成員函數(shù)都聲明為 non-virtual 也很適當。但這種聲明如果不是忽略了 virtual 和 non-virtual 之間的差異,就是過度擔心 virtual 函數(shù)的效率成本。實際上任何 class 如果打算被用來當做一個 base class,都會擁有若干 virtual 函數(shù)。
如果你關(guān)心 virtual 函數(shù)成本,可以了解一下 80-20 法則。這個法則說,一個典型的程序有 80% 的執(zhí)行時間花費在 20% 的代碼身上。此法則十分重要,因為它意味著,平均而言你的函數(shù)調(diào)用中可以有 80% 是 virtual 而不沖擊程序的大體效率。所以當你擔心是否有能力負擔 virtual 函數(shù)的成本之前,請先將心力放在那舉足輕重的 20% 代碼上頭,它才是真正的關(guān)鍵。
另一個常見錯誤是將所有函數(shù)都聲明為 virtual 函數(shù)。有時候這樣做是正確的,例如條款 31 的 interface class。然而這也可能是 class 設(shè)計者缺乏堅定立場的前兆。某些函數(shù)就是不該在 derived class 中被重新定義,果真如此你應(yīng)該將那些函數(shù)聲明為 non-virtual。沒有人有權(quán)利妄稱你的 class 適用于任何人任何事物而他們只需花點時間重新定義你的函數(shù)就可以享受一切。如果你的不變性凌駕于特異性,別害怕說出來。
請記?。?/p>
- 接口繼承和實現(xiàn)繼承不同。在 public 繼承下,derived class 總是繼承 base class 的接口。
- pure virtual 函數(shù)只具體制定接口繼承。
- 簡樸的(非純 impure)virtual 函數(shù)具體指定接口繼承及缺省實現(xiàn)繼承。
- non-virtual 函數(shù)具體制定接口繼承以及強制性實現(xiàn)繼承。
這幾乎和前一個設(shè)計一模一樣,只不過 pure virtual 函數(shù) Airplane::fly 替換了獨立函數(shù) Airplane::defaultFly。其聲明部分表現(xiàn)表的是接口(derived class 必須使用的),其定義部分表現(xiàn)出缺省行為(那是 derived class 可能使用的,但只有在它們明確提出申請時才是)。合并 fly 和 defaultFly,就喪失了 “讓兩個函數(shù)享有不同保護級別” 的機會:習慣上被設(shè)為 protected 的函數(shù)(defaultFly)如今成了 public(因為它在 fly 之中)。
如果成員函數(shù)是個 non-virtual 函數(shù),意味著它并不打算在 derived classes 中有不同的行為。non-virtual 成員函數(shù)所表現(xiàn)的不變性凌駕其特異性,無論 derived class 變得多么特異化,它的行為都不可以改變。
聲明 non-virtual 函數(shù)的目的是為了令 derived class 繼承函數(shù)的接口及一份強制性實現(xiàn)。
來看 Shape::objectID 的聲明:可以想做是 “每個 Shape 對象都有一個用來產(chǎn)生對象識別碼的函數(shù):此識別碼總是采用相同計算方法,該方法由 Shape::objectID 的定義式?jīng)Q定,任何 derived class 都不應(yīng)該嘗試改變其行為”。
pure virtual 函數(shù)、simple(impure) virtual 函數(shù)、non-virtual 函數(shù)之間的差異,使你得以精確指定你想要 derived class 繼承的東西:只繼承接口,或是繼承接口和一份缺省實現(xiàn),或是繼承接口和一份強制實現(xiàn)。由于這些不同的類型的聲明意味著根本意義并不相同的事情,當你聲明你的成員函數(shù)時,必須謹慎選擇。如果你確實履行,應(yīng)該能夠避免經(jīng)驗不足的 class 設(shè)計者最常犯的兩個錯誤。
第一個錯誤是將所有的函數(shù)聲明為 non-virtual。這使得 derived class 沒有余裕的空間進行特化工作。non-virtual 析構(gòu)函數(shù)尤其會帶來問題。當然啦,設(shè)計一個并不相乘為基類的 class 是絕對合理的,既然這樣,將其所有成員函數(shù)都聲明為 non-virtual 也很適當。但這種聲明如果不是忽略了 virtual 和 non-virtual 之間的差異,就是過度擔心 virtual 函數(shù)的效率成本。實際上任何 class 如果打算被用來當做一個 base class,都會擁有若干 virtual 函數(shù)。
如果你關(guān)心 virtual 函數(shù)成本,可以了解一下 80-20 法則。這個法則說,一個典型的程序有 80% 的執(zhí)行時間花費在 20% 的代碼身上。此法則十分重要,因為它意味著,平均而言你的函數(shù)調(diào)用中可以有 80% 是 virtual 而不沖擊程序的大體效率。所以當你擔心是否有能力負擔 virtual 函數(shù)的成本之前,請先將心力放在那舉足輕重的 20% 代碼上頭,它才是真正的關(guān)鍵。
另一個常見錯誤是將所有函數(shù)都聲明為 virtual 函數(shù)。有時候這樣做是正確的,例如條款 31 的 interface class。然而這也可能是 class 設(shè)計者缺乏堅定立場的前兆。某些函數(shù)就是不該在 derived class 中被重新定義,果真如此你應(yīng)該將那些函數(shù)聲明為 non-virtual。沒有人有權(quán)利妄稱你的 class 適用于任何人任何事物而他們只需花點時間重新定義你的函數(shù)就可以享受一切。如果你的不變性凌駕于特異性,別害怕說出來。
請記住:
- 接口繼承和實現(xiàn)繼承不同。在 public 繼承下,derived class 總是繼承 base class 的接口。
- pure virtual 函數(shù)只具體制定接口繼承。
- 簡樸的(非純 impure)virtual 函數(shù)具體指定接口繼承及缺省實現(xiàn)繼承。