作者cppOrz (cppOrz)
看板C_and_CPP
標題Re: [問題] operator[] 和繼承
時間Wed Nov 23 22:58:53 2005
※ 引述《pziyout (pziyout)》之銘言:
: ※ 引述《renderer (rendering)》之銘言:
: : 個人覺得 在這種情況下 不必限制基礎類別一定要是純的抽象類別
: : 並禁止產生基礎類別物件
: : 只要把 int operator[](int) 設成 virtual
: : 並加一個 virtual destructor 即可
: : 對於 virtual method 子類別有權利重新定義其運作
: 將公共繼承架構下的非末端類別都抽象化 可參考 S. Meyers 的
: More Effective C++ : Item 33, 寫的相當長 理由實在難以簡述
: 不過項目的名稱我倒是不會忘記
基本上 ME 條款 33 是對的。不過原 post 的主要問題並不在這裏。
我的看法是,原 post 不應該用 public 的方式繼承 class Base
因為很明顯的原 post 的目的只是精鍊程式碼,應該用
is-implemented-in-terms-of,而不是用 "isa" 的方式來表現,
所以頂多用 protected 繼承就足夠(Base class 的操作也調整為
protected),甚至在此不該用繼承,而該用合成(has-a)的方式
來實現。(另可參考四人幫 DP 的經典著作,優先用合成而非繼承)
簡單地說,不要濫用繼承!雖然它是很有力的機制,但也是兩面刃,
用不得當的話,負擔也很會「有力」。
相關的議題還有 Effective C++ 的條款 36 及條款 37,像原 post
的設計就明顯違反了後者。
大概說一下,首先是條款 37,絕對不要重新定義繼承而來的非虛擬
函式。在物件導向式的設計中,這基本上是錯誤的。因為這種設計
沒有享受到 OO 所提供「多型」的任何好處,而且還帶來的麻煩和
危險。至於詳細的理由大家自己翻書吧,要言之,這麼做根本就違
反 public 繼承的意義。
另外條款 36,這個也很重要,即使看完整本厚厚的 C++ Primer 和
The C++ Programming Language 兩本大部頭,對於這部份多半還是
會迷迷冒冒。簡單引用結論:
◎宣告一個純虛擬函式的目的,是為了讓 derived classes 只繼承
介面。
◎宣告非純虛擬函式的目的,是為了讓 derived classes 繼承該函
式的介面和預設行為。
◎宣告非虛擬函式的目的,是為了讓 derived classes 繼承其介面
及實作。
繼承(與虛擬函式配合)使用的時機也是一門學問。class 代表一種
概念,所謂「介面」,就是指一組設計中,class 與 class 之間(
也就是概念與概念之間)「抽象」(共同、不變)的部份,而「實作」
則是指設計中概念與概念之間「變異」的部份。這三種層次(純虛擬、
非純虛擬、非虛擬)的繼承,就分別表現出對「抽象性」和「變異性」
不同程度的對映。
再次強調,不要拘泥或在語法上鑽牛角尖,而是了解每個特徵(機制)
的原理及應用時機。C++ 並不限制繼承只有上面三種用法,但如果連
上面三種基本用法都沒搞清楚,就自己發明其他古怪的用法,通常是
自找麻煩。
以上三種用法是 OO 設計的基本準則。不代表除此之外的用法都不行,
例如條款 37,雖然它是很不好的設計,至少 C++ 編譯是可以接受的。
(但是這三種基本以外的用法,每一種都有它的額外負擔和必須承受
的風險,如果不是對它的好處和壞處都瞭如指掌,最好是完全避免使
用;尤其在團隊工作時,絕對不要特立獨行。)
另外還有條款 35 中的,「鳥會飛;企鵝是鳥;企鵝會飛?」及「正
方形繼承矩形?」幾個問題的探討,也有助於了解繼承的真實意義和
限制,大家有空多翻翻書吧。
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 59.120.214.120
※ 編輯: cppOrz 來自: 59.120.214.120 (11/23 23:03)
推 UNARYvvv:推推推 11/23 23:42
推 renderer:推 11/24 08:54