2009-07-22 89 views
33

我必須做出關於泛化與多態的決定。C++標準做法:虛擬接口類與模板

那麼這個場景是標準的:我想讓我的單片互相依賴的 代碼更模塊化,乾淨和可擴展。 它仍處於設計原則的變化可行的階段,我認爲它非常可取。

我會介紹純粹的虛擬基類(接口)或模板嗎?

我知道關於模板選項的基礎知識: 間接少,性能更好,更編譯 但 沒有後期綁定,等等。

STL的沒有多大用處(或沒有?)繼承和升壓的確不會。 但我認爲這些都是真正的小型基本工具,程序員每使用這兩行代碼。

我認爲繼承和後期綁定方法對於部署後甚至運行時可更新的代碼和功能的大塊代碼和功能的插件風格更爲合理。

那麼我的情況是介於兩者之間。

我不需要在運行時交換代碼段,編譯時間很好。 通常它也是一個非常重要且經常使用的功能塊,它在邏輯上不可分割成大塊。

這讓我傾向於模板解決方案。 對我來說,它也看起來更清潔。

是否有任何大的不良影響,因此仍然接口的方式 去?他們什麼時候不是? 哪些更符合標準C++風格?

我知道這是主觀的接壤,但我在 一些經驗很感興趣。我沒有Scott Meyers有效的C++ 的副本,所以我將我的希望寄託在你們身上:)

回答

20

你基本上是對的,動態多態(繼承,虛擬)通常是正確的選擇,當類型應該是允許在運行時更改(例如在插件體系結構中)。如果只能在編譯時更改類型,靜態多態(模板)纔是更好的選擇。

到模板的唯一潛在的缺點是:1)它們通常具有在報頭中定義(這意味着更多的代碼被包含(#include)),並且這常常導致更慢的編譯時間。

但是在設計方面,我無法在使用模板時發現任何問題。

哪個更符合標準C++ 樣式?

要看什麼 「標準C++風格」 是。 C++標準庫使用了一切。STL爲所有事物使用模板,稍微陳舊的IOStreams庫使用繼承和虛函數,當然,從C繼承的庫函數也不使用。

這些天,模板是迄今爲止最流行的選擇,但我不得不說這是最「標準」的方法。

+3

嗯,我確實看到使用模板而不是接口的一個問題:需求是完全隱含的。當你必須實現純虛函數時,你會得到它的確切簽名。但是當你看到像_AllocT或Iter這樣的模板類型時,你不知道你的類需要什麼類型,甚至不需要是類。你唯一需要知道的方法是尋找一份關於它的體面文檔,我今天嘗試創建自己的stl兼容分配器類時遇到了麻煩。 – Virus721 2015-06-11 15:23:51

+3

「你只有通過尋找一個體面的文檔來了解它」 - 或者通過編譯和查看編譯器抱怨無法找到哪些函數,是的。此外,概念旨在解決這個問題。 (即使它是一個界面,你仍然需要找到體面的文檔。知道要覆蓋哪些函數是不夠的。你還需要知道它們的語義應該是什麼,並且界面不會告訴你)。儘管如此,你是對的。語言支持這兩個原因是有原因的。 :) – jalf 2015-06-11 15:27:27

8

這是一種虛假的反對意見。是的,繼承和虛擬功能的主要用途是iostreams,這是非常古老的,並與其他std庫的風格非常不同。

但許多「最酷」的現代C++庫(如boost)都使用運行時多態,它們只是使用模板來使它更方便使用。

boost::anystd::tr1::function(以前也來自boost)是很好的例子。

它們都是單項容器,它的具體類型在編譯時是未知的(這在any中尤其明顯,因爲它有自己的一種動態轉換運算符來獲取值)。經典的面向對象的多態性

+0

+1您的回答非常有啓發性,但遵守問題我接受了賈爾夫的建議。謝謝。 – AndreasT 2009-08-24 13:54:50

9

性質:

  • 對象在運行時約束;這是更靈活的,但也消耗更多的資源(CPU)在運行時
  • 強打字帶來更多的類型安全,但需要dynamic_cast(和它的潛力吹到客戶的臉上)可能很容易彌補
  • 可能更廣泛地瞭解和理解,但「經典」深繼承層次看起來可怕我編譯時多態性

屬性的模板:

  • 編譯時綁定允許更積極optimizat但會妨礙運行時的靈活性
  • 鴨式打字看起來似乎更笨拙,但失敗通常是編譯時失敗
  • 有時可能難以閱讀和理解;沒有概念,編譯器診斷有時可能變得令人生氣

請注意,沒有必要爲任何一個決定。你可以自由地混合和混合他們(和許多其他成語和範例)。通常,這會導致非常令人印象深刻的(和富有表現力的)代碼。 (例如,請參閱類型擦除的內容。)要想知道通過巧妙地混合範例有什麼可能,您可能需要瀏覽Alexandrescu的「Modern C++ Design」。

0

從我的角度來看,這是你最擅長的。如果您有更多面向對象使用面向對象的經驗。如果您有更多關於泛型的經驗,請使用泛型。

這兩種技術都有一些等同的模式,這些模式也意味着你可以使用很多東西。 EG戰略OO與泛型中的策略或OO中的模板方法與泛型中常見的循環模板模式。

如果您計劃的重構生產代碼已經正常工作,但結構有點臭。不要以此爲藉口來使用一些新的設計技術,因爲在一兩年後,如果您更好地理解技術,您可能會後悔如何重構代碼。在學習技巧時,引入新的不靈活性非常容易。如果你不熟練使用某種技術,則目的是改進現有代碼的設計。你如何知道自己正在改進設計,而不是在代碼中構建一個大的陰莖符號。

就我個人而言,我在OO方面更好,並傾向於支持它,因爲我知道我可以製作易於理解且大多數人可以改變的乾淨設計。大多數通用代碼我正確的目標是與其他通用代碼進行交互,例如編寫迭代器或用於算法的通用函數。

4

已經鏟了我的盤子裏一點經驗後,有模板一些事情,我不喜歡: 有跡象表明,從一個可用的語言資格模板元編程一定的缺點:

  • 可讀性:太多的括號,太多的非語言的執行(因此濫用)公約
  • 的人經歷的編程語言通常的進化,模板是不可讀UND難以理解(只看提升BGL)
  • 有時感覺就像是有人試圖到w在awk中成爲一個C++代碼生成器。
  • 編譯器錯誤消息是cl(完全)ed廢話
  • 需要太多的hacks(其中大多數補救在C++ 0x)以獲得一些基本的「語言」功能。
  • 否實現文件中的模板導致僅標頭庫(這是一個雙面劍)
  • 通常IDE的代碼完成功能對模板沒有多大幫助。
  • 在MPL中做大事似乎「討厭」,找不到別的詞。模板化代碼的每一行都會對該模板類型產生約束,這些約束將以文本替換方式強制執行。繼承層次結構中存在固有的語義,模板結構中沒有任何內容。就像所有東西都是void *一樣,編譯器試圖告訴你是否會出現段錯誤。

就這麼說,我在基本實用程序和庫上使用它非常成功。用它編寫高級功能或硬件相關的東西,似乎並不適合我。 含義我模板我的積木,但建立房子的經典方式。

0

我在我的大代碼庫中都使用這兩個函數。在編譯時知道類型時,我使用模板進行設計,只有在運行時才知道類型,我使用虛函數。我發現虛擬函數更容易編程,並且稍後可以更容易閱讀,但有時候性能是至關重要的,而模板化的多態性(如果你真的可以稱之爲多態性)可以內聯的真正幫助。