不要重新定義繼承來的非虛函式

2021-08-04 02:29:30 字數 1350 閱讀 5427

–effective c++ item 36

class b 

};class d: public b

};

對於這個繼承體系,有這樣的**

d x;

b* pb =

&x;pb->foo(); // test_b

d* pd =

&x;pd->foo(); // test_d

驚訝的發現,兩個語句的行為不一樣..兩者所呼叫的函式相同,物件也相同,因此行為也應該相同…

但是,事實是這個函式是個non-virtual函式 而且子類d又有自己的版本,這樣行為就不是一樣的了.

pb->foo();      // 呼叫了b::foo()

pd->foo(); // 呼叫了d::foo()

造成它們不同行為的原因是,non-virtual 函式如b::foo()和d::foo()都是靜態繫結.意思是,由於pb被宣告為乙個pointer-to-b,通過pb呼叫的non-virtual 函式永遠是b所定義的版本,即使pb指向乙個」b派生之class」的物件..

不過virtual函式是動態繫結,所以它不會受苦…(霧

如果foo()是個virtual 函式,不論是通過pb還是pd呼叫foo(),都會導致呼叫d::foo(),因為pb和pd都是指向真正的乙個型別為d的物件.

實際上,為多型基類宣告virtual 析構函式是這個規範的乙個特殊案例…

首先得明確兩個觀點

第乙個很好解釋,當使用public繼承意味著一種is-a關係.意思是每乙個型別為d的物件同時也是乙個型別為b的物件,反之不成立.

由於non-virtual函式在成員函式中表現明顯的不變性(在子類中不可改變其介面和實現).所以如果成員函式是個non-virtual 函式,意味著它並不打算在子類中有不同的行為.

現在,如果d重新定義foo(), 設計就會出現矛盾.如果d真的有必要實現出於b不同的foo(),那麼」每個d都是乙個b」就不為真..因此,就不應該用public形式繼承b.

另一方面, 如果d真的必須以public方式繼承b,並且要有於b不同的foo()..這樣,foo()就應該宣告為virtual 函式.

不論哪乙個觀點,結論都相同: 任何情況下都不應該重新定義乙個繼承而來的non-virtual 函式..

決不要重新定義繼承而來的非虛函式

有兩種方法來看待這個問題 理論的方法和實踐的方法。讓我們先從實踐的方法開始。畢竟,理論家一般都很耐心。假設類d公有繼承於類b,並且類b中定義了乙個公有成員函式mf。mf的引數和返回型別不重要,所以假設都為void。換句話說,我這麼寫 class b class d public b 甚至對b,d或m...

重新定義繼承而來的非虛函式

在一次應聘過程中,負責技術的招聘人員提出了乙個實際開發中遇到的問題 class base class derive public base 結果編譯卻發現錯誤。他想知道是怎麼回事。當時怎麼看怎麼像函式過載 公有繼承嘛 似乎沒什麼問題呀,只好說不知道。後來終於在 effective c 2nd 中找到...

條款37 決不要重新定義繼承而來的非虛函式

class b class d public b 甚至對b,d或mf一無所知,也可以定義乙個型別d的物件x,d x x是型別d的乙個物件 那麼,如果發現這麼做 b pb x 得到x的指標 pb mf 通過指標呼叫mf 和下面這麼做的執行行為不一樣 d pd x 得到x的指標 pd mf 通過指標呼叫...