簡體   English   中英

多重繼承指針比較

[英]Multiple inheritance pointer comparison

我有一個Derived類,它直接從兩個基類Base1Base2繼承。 我想知道一般來說,將指針與基類進行比較以確定它們是否是相同的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 ++的角度來看它是否可以。 我實際上從不對指針進行任何操作,我只是想知道它們是否指向同一個對象。

編輯:如果我能保證p1p2指向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 *)

一個更好的解決方案是重載Base1Base2Derived==運算符。

假設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?

這些都不是一個好的解決方案。 第一個不會編譯,因為你無法比較不相關類型的指針。 第二個也不會編譯(除非Base1Base2通過繼承相關),原因相同:你不能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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM