Re: [問題] 多重繼承疑惑
※ 引述《QQ29 (我愛阿蓉)》之銘言:
: 有點走火入魔了....
: 現在試著了解一下多重繼承
: http://nopaste.csie.org/5f8d3
: 這是不使用虛擬繼承跑跑看的結果
: 發現new的 和 直接用物件 印出來的結果竟然有差異
: 先拿物件的方式印來看
: A::a 分給B::a 和C::a 因為D先繼承C 所以A::a和C::a是同一個變數
: 位址一樣...可以接受(這也是更改繼承順序得到的心得....)
: 但是我用new來看結果發現A::a 是一個 B::a和C::a竟然是一樣 很奇怪
咳咳...你用了錯誤的轉型。
先來看看你如何把 p 轉型成 B*:
cout<<&( ((B*)&p)->B::a )<<endl;
首先你用 &p 就錯了...&p 是 B**,也就是指向指標的指標,而不是指向 A 的指標。
因此至少你要改成:
cout<<&( ((B*)p)->B::a )<<endl;
不幸的是,這樣做的結果不如預期。你的想法可能是:
「p 的型別雖然是 A*,但它實際上指向 D 物件,我希望取出這個 D 物件中,
屬於 B 的那部份」
OK...現在,假設你是 compiler,你要怎麼知道「p 實際指向一個 D 物件」?
事實上是 compiler 根本無法知道 p 指向什麼,因為同樣的 code 我們可以這樣寫:
void print(A* p)
{
cout<<&( ((B*)p)->B::a )<<endl;
}
print(new A);
print(new B);
print(new C);
print(new D);
你注意到了嗎...compiler 根本就無法知道他接的 p 是來自於哪一種物件,
所以你寫 (B*)p,其實就只是把 p 的內容「直接」當成一個指向 B 的指標,
而不是「p 其實是個 D*,而 D 裡面有 B,所以應該取出 D 裡面的那份 B」,
compiler 不知道 p 真正指向什麼的。
如果上面的東西你能理解,我們繼續來看要怎麼解決這問題。
回想一下我上一篇提到 virtual function,想一下:
如果 a 這個東西並不是某個 member variable 而是 member function,
為什麼 compiler 會知道 p 的真正形別?
void print(A* p)
{
// 我不知道 p 指向什麼,但我可以從 vtable 中找出 function pointer
cout << p->a() << endl;
}
print(new A); // 使用 A::a()
print(new D); // 使用 D::a()
是的,答案就是利用 vtable。事實上 compiler 在 vtable 中,
除了加入 function pointer 之外,還會記錄這個 vtable 屬於哪個 class。
而 C++ 提供了 dynamic_cast 這個轉型用的 operator,幫助你在這種情況下轉形。
所以你的 code 要改成這樣:
A *p =(B*) new D;
cout << &(p->A::a) << endl;
cout << &( (dynamic_cast<B*>(p) )->B::a) << endl;
cout << &( (dynamic_cast<C*>(p) )->C::a) << endl;
dynamic_cast<B*>(p) 的意思是說,我不知道 p 真正指向哪一種 class,
但我想利用 vtable 中的資訊,試圖把 p 「正確地」轉成 B*。
改用 dynamic_cast 後,我想結果應該會符合你的預期。
: 於是我把C和B class都加上virtual public: http://nopaste.csie.org/3e47e
: 物件的方式印出 全部都一樣 恩~可以接受
: 但是用new的方式印
: 奇怪A::a 又和其他不一樣了?
: 是我cout寫錯嗎 new的和用物件的方式怎麼會有差異
: 請教一下
: 謝謝
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.112.29.108
推
02/11 18:57, , 1F
02/11 18:57, 1F
→
02/11 19:08, , 2F
02/11 19:08, 2F
→
02/11 20:42, , 3F
02/11 20:42, 3F
→
02/11 20:43, , 4F
02/11 20:43, 4F
推
02/11 20:49, , 5F
02/11 20:49, 5F
推
02/12 00:26, , 6F
02/12 00:26, 6F
討論串 (同標題文章)