看板 C_and_CPP 關於我們 聯絡資訊
開發平台(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)
angleevil:是不是因為你沒加虛擬解構函式? 02/14 09:48
angleevil:虛擬解構函式出現undefined reference to vtable for A 02/14 09:54
angleevil:超哥不好意思,麻煩你把這三段刪除掉,因為那不是問題點 02/14 10:06
james732:不會啊,你沒講的話,我都忘了虛擬解構函式這東西了 02/14 10:13
shadow0326:應該是因為把A ptr cast成B的關係, A和B並無derive 02/14 10:30
shadow0326:直接call ((C*)p)->sub() 就是對的 02/14 10:31
cuteclare:cout << ((B *)((C *)p))->sub() << endl; 02/14 10:36
cuteclare:orz沒看後面的code就直接推文...我錯了~><~ 02/14 10:38
angleevil:基本上碰到這種做法,我會使用抽像類別讓A,B去繼承 02/14 10:41
angleevil:http://codepad.org/LLb6FVot 可是我對c++ OO其實很不熟 02/14 10:42
angleevil:所以A,B class中的運算式暫時註解掉.effective c++有提 02/14 10:44
angleevil:到詳細做法,只是我手上沒書.看看有沒有其他人可以補充 02/14 10:44
shadow0326:可以用dynamic_cast<B*>硬幹, 不過超低效 XD 02/14 10:45
cuteclare:oh 對唷...完全忘記還有dynamic_cast 02/14 10:49
angleevil:樓上,我硬幹過了,結果是失敗. 02/14 10:50
cuteclare:樓上 cout << (dynamic_cast<B*>(p))->sub() << endl; 02/14 10:52
cuteclare:我這樣可以欸.我是用gcc version 4.5.2 (GCC) 02/14 10:53
angleevil:真的要用,我會直接用(dynamic_cast<C*>(p))->sub().畢竟 02/14 10:57
angleevil:c本來就有B的功能了.不過我學起來,謝謝兩位. 02/14 10:58
我沒有強調一個重點,這兩個 parent class 裡,有一個不是我寫的 它其實是 MFC 的 CDialog class 所以沒辦法在 class A, class B 上面再加一層 Abstract 另外,我一直不知道 dynamic_cast 什麼時候會用到,這篇總算讓我懂了 XD
shadow0326:所以我說直接call ((C*)p)->sub() 就是對的, 只是不知 02/14 11:00
shadow0326:道原本要特地cast成B的use case是什麼就是了 @_@ 02/14 11:01
哎呀,是我沒有說清楚 其實我的情況還會有一個 D, 同樣繼承 A 與 B 在程式執行的時候會藉由建立 C 或 D 的實體,來做不同的事情 而且工作會改變的是 B 介面的部份 用原本的例子改起來就像這樣 http://ideone.com/RUFzz 我有點驚訝這兩行程式碼竟然可以正確執行,得到我要的答案 A *p = new D; cout << ((C *)p)->sub() << endl; 不過總覺得看起來好怪異...
angleevil:盡量還是用c++的轉型,c的轉型不是很適合OO 02/14 11:12
NIKE74731:angleevil大的作法會構成Death Diamond吧@@ 02/14 11:18
shadow0326:可以正確執行是因為C,D剛好成員數一樣, 這寫法也太不健 02/14 11:18
shadow0326:康了 @_@ 架構上要整個改過比較好吧 02/14 11:18
我也覺得很恐怖,所以才會想要轉成B 大概是類似這樣的感覺,會比轉成C還要合理 A *p = new C; ((B *)p)->sub(); delete p; p = new D; ((B *)p)->sub();
ADF:static_cast< C* >( p )->sub(); 02/14 11:50
ADF:別用c的轉型 如果你要把B的pointer換成C且C的定義還沒出現 02/14 11:54
ADF:p轉型後資料會敗壞 02/14 11:56
angleevil:喔!終於搞懂NIKE74731的意思,當然class c不要同時繼承 02/14 12:31
angleevil:A和B是最好的,本人不是很專精OO的繼承技巧,大家也可以提 02/14 12:33
angleevil:出改進的方法.如果一定要有好的方法,那就是宣告B 物件 02/14 12:35
angleevil:然後將p dynamic_cast成B *.最後assign給B 物件. 02/14 12:37
NIKE74731:我很好奇既然是MFC的Class 竟然會有pure virtual func. 02/14 15:51
NIKE74731:這樣一來要使用這個類別就必須繼承且實作該virtual func 02/14 15:52
NIKE74731:MFC有這種Class嗎 02/14 15:53
唔,其實 CDialog 並沒有 pure virtual function (應該吧?) 我承認這裡是我的例子舉得不好XD
angleevil:其實如果你只要引用B 部分的功能,直接在C理面宣告 02/15 09:30
angleevil:B* getBptr,然後用一個成員函式包起來.ex: int sub(){ 02/15 09:32
angleevil:return getBptr->sub()} 這樣就可以了 02/15 09:33
可是我需要B的介面...XD ※ 編輯: james732 來自: 101.13.7.106 (02/15 09:46)
NIKE74731:那如果是C繼承A A繼承B 可解嗎? 02/15 17:33
angleevil:他應該care會很肥,所以一直不願意如此去解決 02/15 21:16
angleevil:可是超哥,我必須要說,你第三個方法基本上跟我在09:30 02/16 08:53
angleevil:提到方法差不多,晚點看我有沒有時間舉例出來 02/16 08:54
mingtai1:大量重複new/delete易造成memory fragmentation 不太建議 02/17 22:41
angleevil:http://codepad.org/rifhR4RY <--把兩個抽象類別合起來 02/22 09:59