[問題] 多重繼承與多型

看板C_and_CPP作者 (好人超)時間14年前 (2012/02/14 08:27), 編輯推噓7(7037)
留言44則, 7人參與, 最新討論串1/3 (看更多)
開發平台(Platform): (Ex: VC++, GCC, Linux, ...) Windows Visual C++ 問題(Question): 如同程式碼A,我的Class C同時繼承了Class A與Class B 大部份時候我希望用Class A的介面去存取(例中的add) 但在部份的情況下,我還是有必要用Class B的介面(例中的sub) sub函式的預期結果是5,但程式碼A的寫法,卻會跑出7這個怪值 我想是因為虛擬函式的偏移位置不對,因為加上一些訊息輸出後 可以發現第二次呼叫的仍然是add() (如程式碼 A+msg) 我試著修改成程式碼B,雖然可以正確的跑出5,但我不知道這種寫法是不是安全的 (呃,看起來顯然不是) 因此我想問的是,像這種情況: 我有個Class A的指標,指向Class C的Object 而且我確定Class C有繼承Class B,並且實作了虛擬函式 那我要怎麼利用這個Class A指標,去呼叫Class B提供的虛擬函式? 預期的正確結果(Expected Output): 7 5 10 錯誤結果(Wrong Output): 7 7 10 程式碼(Code):(請善用置底文網頁, 記得排版) A : http://ideone.com/cGHsx A+msg : http://ideone.com/XGWnb B : http://ideone.com/GVNbf (與A的差別在第30與第37行) -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 101.13.63.240 ※ 編輯: james732 來自: 101.13.63.240 (02/14 08:34)

02/14 09:48, , 1F
是不是因為你沒加虛擬解構函式?
02/14 09:48, 1F

02/14 09:54, , 2F
虛擬解構函式出現undefined reference to vtable for A
02/14 09:54, 2F

02/14 10:06, , 3F
超哥不好意思,麻煩你把這三段刪除掉,因為那不是問題點
02/14 10:06, 3F

02/14 10:13, , 4F
不會啊,你沒講的話,我都忘了虛擬解構函式這東西了
02/14 10:13, 4F

02/14 10:30, , 5F
應該是因為把A ptr cast成B的關係, A和B並無derive
02/14 10:30, 5F

02/14 10:31, , 6F
直接call ((C*)p)->sub() 就是對的
02/14 10:31, 6F

02/14 10:36, , 7F
cout << ((B *)((C *)p))->sub() << endl;
02/14 10:36, 7F

02/14 10:38, , 8F
orz沒看後面的code就直接推文...我錯了~><~
02/14 10:38, 8F

02/14 10:41, , 9F
基本上碰到這種做法,我會使用抽像類別讓A,B去繼承
02/14 10:41, 9F

02/14 10:42, , 10F
http://codepad.org/LLb6FVot 可是我對c++ OO其實很不熟
02/14 10:42, 10F

02/14 10:44, , 11F
所以A,B class中的運算式暫時註解掉.effective c++有提
02/14 10:44, 11F

02/14 10:44, , 12F
到詳細做法,只是我手上沒書.看看有沒有其他人可以補充
02/14 10:44, 12F

02/14 10:45, , 13F
可以用dynamic_cast<B*>硬幹, 不過超低效 XD
02/14 10:45, 13F

02/14 10:49, , 14F
oh 對唷...完全忘記還有dynamic_cast
02/14 10:49, 14F

02/14 10:50, , 15F
樓上,我硬幹過了,結果是失敗.
02/14 10:50, 15F

02/14 10:52, , 16F
樓上 cout << (dynamic_cast<B*>(p))->sub() << endl;
02/14 10:52, 16F

02/14 10:53, , 17F
我這樣可以欸.我是用gcc version 4.5.2 (GCC)
02/14 10:53, 17F

02/14 10:57, , 18F
真的要用,我會直接用(dynamic_cast<C*>(p))->sub().畢竟
02/14 10:57, 18F

02/14 10:58, , 19F
c本來就有B的功能了.不過我學起來,謝謝兩位.
02/14 10:58, 19F
我沒有強調一個重點,這兩個 parent class 裡,有一個不是我寫的 它其實是 MFC 的 CDialog class 所以沒辦法在 class A, class B 上面再加一層 Abstract 另外,我一直不知道 dynamic_cast 什麼時候會用到,這篇總算讓我懂了 XD

02/14 11:00, , 20F
所以我說直接call ((C*)p)->sub() 就是對的, 只是不知
02/14 11:00, 20F

02/14 11:01, , 21F
道原本要特地cast成B的use case是什麼就是了 @_@
02/14 11:01, 21F
哎呀,是我沒有說清楚 其實我的情況還會有一個 D, 同樣繼承 A 與 B 在程式執行的時候會藉由建立 C 或 D 的實體,來做不同的事情 而且工作會改變的是 B 介面的部份 用原本的例子改起來就像這樣 http://ideone.com/RUFzz 我有點驚訝這兩行程式碼竟然可以正確執行,得到我要的答案 A *p = new D; cout << ((C *)p)->sub() << endl; 不過總覺得看起來好怪異...

02/14 11:12, , 22F
盡量還是用c++的轉型,c的轉型不是很適合OO
02/14 11:12, 22F

02/14 11:18, , 23F
angleevil大的作法會構成Death Diamond吧@@
02/14 11:18, 23F

02/14 11:18, , 24F
可以正確執行是因為C,D剛好成員數一樣, 這寫法也太不健
02/14 11:18, 24F

02/14 11:18, , 25F
康了 @_@ 架構上要整個改過比較好吧
02/14 11:18, 25F
我也覺得很恐怖,所以才會想要轉成B 大概是類似這樣的感覺,會比轉成C還要合理 A *p = new C; ((B *)p)->sub(); delete p; p = new D; ((B *)p)->sub();

02/14 11:50, , 26F
static_cast< C* >( p )->sub();
02/14 11:50, 26F

02/14 11:54, , 27F
別用c的轉型 如果你要把B的pointer換成C且C的定義還沒出現
02/14 11:54, 27F

02/14 11:56, , 28F
p轉型後資料會敗壞
02/14 11:56, 28F

02/14 12:31, , 29F
喔!終於搞懂NIKE74731的意思,當然class c不要同時繼承
02/14 12:31, 29F

02/14 12:33, , 30F
A和B是最好的,本人不是很專精OO的繼承技巧,大家也可以提
02/14 12:33, 30F

02/14 12:35, , 31F
出改進的方法.如果一定要有好的方法,那就是宣告B 物件
02/14 12:35, 31F

02/14 12:37, , 32F
然後將p dynamic_cast成B *.最後assign給B 物件.
02/14 12:37, 32F

02/14 15:51, , 33F
我很好奇既然是MFC的Class 竟然會有pure virtual func.
02/14 15:51, 33F

02/14 15:52, , 34F
這樣一來要使用這個類別就必須繼承且實作該virtual func
02/14 15:52, 34F

02/14 15:53, , 35F
MFC有這種Class嗎
02/14 15:53, 35F
唔,其實 CDialog 並沒有 pure virtual function (應該吧?) 我承認這裡是我的例子舉得不好XD

02/15 09:30, , 36F
其實如果你只要引用B 部分的功能,直接在C理面宣告
02/15 09:30, 36F

02/15 09:32, , 37F
B* getBptr,然後用一個成員函式包起來.ex: int sub(){
02/15 09:32, 37F

02/15 09:33, , 38F
return getBptr->sub()} 這樣就可以了
02/15 09:33, 38F
可是我需要B的介面...XD ※ 編輯: james732 來自: 101.13.7.106 (02/15 09:46)

02/15 17:33, , 39F
那如果是C繼承A A繼承B 可解嗎?
02/15 17:33, 39F

02/15 21:16, , 40F
他應該care會很肥,所以一直不願意如此去解決
02/15 21:16, 40F

02/16 08:53, , 41F
可是超哥,我必須要說,你第三個方法基本上跟我在09:30
02/16 08:53, 41F

02/16 08:54, , 42F
提到方法差不多,晚點看我有沒有時間舉例出來
02/16 08:54, 42F

02/17 22:41, , 43F
大量重複new/delete易造成memory fragmentation 不太建議
02/17 22:41, 43F

02/22 09:59, , 44F
http://codepad.org/rifhR4RY <--把兩個抽象類別合起來
02/22 09:59, 44F
文章代碼(AID): #1FEQgEia (C_and_CPP)
文章代碼(AID): #1FEQgEia (C_and_CPP)