[英]Multiple inheritance pointer comparison
我有一個Derived
類,它直接從兩個基類Base1
和Base2
繼承。 我想知道一般來說,將指針與基類進行比較以確定它們是否是相同的Derived
對象是否安全:
Base1* p1;
Base2* p2;
/*
* Stuff happens here. p1 and p2 now point to valid objects of either their
* base type or Derived
*/
//assert(p1 == p2); //This is illegal
assert(p1 == static_cast<Base1*>(p2)); //Is this ok?
assert(static_cast<Derived*>(p1) == static_cast<Derived*>(p2)); //How about this?
指針保證有效,但不一定指向Derived
對象。 我的猜測是這可能很好,但我想從技術C ++的角度來看它是否可以。 我實際上從不對指針進行任何操作,我只是想知道它們是否指向同一個對象。
編輯:如果我能保證p1
和p2
指向Derrived
對象似乎是安全的。 我基本上想知道如果它們不是 - 如果一個或兩個都指向一個基礎對象,它是否安全,那么比較是否會失敗? 同樣,我可以保證指針有效(即, p1
永遠不會指向Base2
對象,反之亦然)
嗯,不,它不會起作用。
我個人是學習的忠實粉絲,所以這里有一個:
#include <iostream>
class Base1
{
public:
Base1()
{
numberBase1 = 1;
}
int numberBase1;
};
class Base2
{
public:
Base2()
{
numberBase2 = 2;
}
int numberBase2;
};
class Derived : public Base1, public Base2
{
public:
Derived()
{
numberDerived = 3;
}
int numberDerived;
};
int main()
{
Derived d;
Base1 *b1 = &d;
Base2 *b2 = &d;
std::cout << "d: " << &d << ", b1: " << b1 << ", b2: " << b2 << ", d.numberDerived: " << &(d.numberDerived) << std::endl;
return 0;
}
我的計算機上的一個貫穿輸出:
d: 0035F9FC, b1: 0035F9FC, b2: 0035FA00, d.numberDerived: 0035FA04
Soo ..如果我們將d的地址定義為0,則b1為0,b2為+4,d的數量為+8。 這是因為我機器上的int長4個字節。
基本上,您必須查看C ++內部表示類的布局:
Address: Class:
0 Base1
4 Base2
8 Derived
總而言之,實例化Derived類將為派生類的基類分配空間,最后為派生對象本身騰出空間。 由於這里有3個整數,因此它將是12個字節。
現在,你問的問題(除非我誤解了一些東西)是你可以比較不同基類指針的地址,看看它們是否指向同一個對象,答案是否定的 - 至少不是直接的,在我的例子中,b1將指向0035F9FC,而b2將指向0035FA00。 在C ++中,這種偏移都是在編譯時完成的。
你可以用RIIA和sizeof()來做一些魔術,並確定偏移量b2應該與b1相當多少,但是你會遇到各種其他問題,比如虛擬。 簡而言之,我不推薦這種方法。
一個更好的方法是投射到Derived *,就像ialiashkevich所說的那樣,如果你的對象不是Derived *的實例,那就會產生問題。
(免責聲明;我在3 - 4年內沒有使用過C ++,所以我的游戲可能有些偏差。溫柔:))
在比較之前投射到Derived*
是正確的方法。
有一個類似的話題: C ++指針多繼承的樂趣
簡短的回答是否定的,這通常不是一個好主意。
注意 :這假設您希望所有類的自定義等效,如果要檢查它們是否是同一個對象,最好這樣做(Derived *)
。
一個更好的解決方案是重載Base1
, Base2
和Derived
的==
運算符。
假設Base1
有1個參數param1
用於相等,而Base2
有另一個參數param2
用於相等:
virtual bool Base1::operator==(object& other){
return false;
}
virtual bool Base1::operator==(Base1& other)
{
return this.param1 == other.param1;
}
virtual bool Base2::operator==(object& other){
return false;
}
virtual bool Base2::operator==(Base2& other)
{
return this.param2 == other.param2;
}
virtual bool Derived::operator==(object& other){
return false;
}
virtual bool Derived::operator==(Derived& other){
return this.param1 == other.param1 && this.param2 == other.param2;
}
virtual bool Derived::operator==(Base1& other){
return this.param1 == other.param1;
}
virtual bool Derived::operator==(Base2& other){
return this.param2 == other.param2;
}
那么,找出你想要的最短途徑是:
assert(dynamic_cast<void*>(p1) == dynamic_cast<void*>(p2));
動態轉換為void*
有效地將給定指針向下轉換為其最派生類,因此如果兩者都指向同一對象,則保證斷言不會失敗。
實際上, 動態轉換有實際用途來使指針無效 ......
編輯:要回答這個問題的編輯,比較是不是安全。 請考慮以下代碼:
Base2 b2;
Base1 b1;
assert(static_cast<Derived*>(&b1) == static_cast<Derived*>(&b2)); // succeeds!
兩個不同庫的內存布局類似於Derived
的內存布局(在常見實現上 - 堆棧與堆相反)。 第一個static_cast
將指針保持原樣,但是第二個指針將指針sizeof(Base1)
移回,因此現在它們都指向&b1
,並且斷言成功 - 即使對象不同。
只有在確定演員表是正確的時候才應該使用static_cast
。 這不是你的情況,所以你必須使用dynamic_cast
,可能如上所述。
基於這個問題,它似乎是無效的: C ++的多重繼承是如何實現的?
基本上,由於對象在內存中的布局方式,對Base1*
或Base2*
強制轉換Base2*
導致指針的突變,我無法在沒有dynamic_cast
情況下在運行時任意反轉,我想避免。 感謝大家!
使用dynamic_cast
,注意NULL。
#include <cassert>
struct Base1 { virtual ~Base1() {} };
struct Base2 { virtual ~Base2() {} };
struct Derived : Base1, Base2 {};
bool IsEqual(Base1 *p1, Base2 *p2) {
Derived *d1 = dynamic_cast<Derived*>(p1);
Derived *d2 = dynamic_cast<Derived*>(p2);
if( !d1 || !d2 ) return false;
return d1 == d2;
}
int main () {
Derived d;
Base1 *p1 = &d;
Base2 *p2 = &d;
Base1 b1;
Base2 b2;
assert(IsEqual(p1, p2));
assert(!IsEqual(p1, &b2));
assert(!IsEqual(&b1, p2));
assert(!IsEqual(&b1, &b2));
}
assert(p1 == p2); //This is illegal
assert(p1 == static_cast<Base1*>(p2)); //Is this ok?
assert(static_cast<Derived*>(p1)
== static_cast<Derived*>(p2)); //How about this?
這些都不是一個好的解決方案。 第一個不會編譯,因為你無法比較不相關類型的指針。 第二個也不會編譯(除非Base1
和Base2
通過繼承相關),原因相同:你不能static_cast
到一個不相關類型的指針。
第三種選擇是臨界 。 也就是說,它不正確,但它在許多情況下都會起作用(只要繼承不是虛擬的)。
比較身份的正確方法是使用dynamic_cast
到派生類型並檢查null:
{
Derived *tmp = dynamic_cast<Derived*>(p1);
assert( tmp && tmp == dynamic_cast<Derived*>(p2) );
{
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.