2012-04-02 125 views
13

考慮兩個指針如何檢查兩個指針​​是否指向同一個對象?

A* a; 
B* b; 

A和B是多態的類。 如何檢查a和b是否指向同一個對象?

更準確地說,我們指定a和b指向同一個對象,如果存在類型d的一些對象d,使得兩個*和* b是d的類層次結構的某個地方。

我建議如下解決方案:

dynamic_cast<void*>(a) == dynamic_cast<void*>(b) 

實際上,根據該標準,

dynamic_cast<void*>(v) 

產量「的指針最派生對象用V指向(n3242.pdf。 :§5.2.7 - 7)。 如果兩者的派生最相同的對象,那麼指針指向同一個對象。

我很確定它應該始終從實際的角度正確工作。但理論上,乍看之下,所提出的平等似乎會產生誤判,例如,如果b指向A的第一個成員(而不是A的祖先)。雖然A和它的成員實際上不可能獲得相同的地址,因爲A的虛擬表指針應該位於該成員之前,但該標準並沒有強制虛擬表並且沒有提及類佈局。

所以,我的問題是:

  1. 是所提出的解決方案,從標準的角度看是否正確?

  2. 有沒有關於私人(受保護)繼承或cv資格的警告?

  3. 有沒有更好的解決方案?

[編輯]

我試圖提出一些例子示出了相對複雜的場景。在這種情況下,動態交叉轉換和靜態轉換是不明確的。

// proposed impplementation: 
template<typename P, typename Q> 
bool test_ptrs(const P* p, const Q* q) 
{ 
    return (dynamic_cast<const void*>(p) == dynamic_cast<const void*>(q)); 
} 


struct Root 
{ 
    virtual ~Root(){}; 
}; 

struct A: public Root // nonvirtually 
{ 
}; 

struct B: public Root // nonvirtually 
{ 
}; 

struct C: public A, B // nonvirtual diamond started with Root 
{ 
    Root another_root_instance; 
}; 

int main() 
{ 
    C c; 

    A* pa= &c; 
    B* pb= &c; 

    bool b = (dynamic_cast<void*>(pa) == dynamic_cast<void*>(pb)); 

    Root* pra= dynamic_cast<Root*> (pa); 
    Root* prb= dynamic_cast<Root*> (pb); 

    //Root* prc= dynamic_cast<Root*> (&c); // runtime error, ambiguous cast 
    Root* prr= dynamic_cast<Root*>(pra); 

    Root* pcar= dynamic_cast<Root*>(pra); 
    Root* pcbr= dynamic_cast<Root*>(prb); 

    if(
     test_ptrs(pa, pb) 
     && test_ptrs(pra, prb) 
     && !test_ptrs(pa,&c.another_root_instance) 
    ) 
    { 
    printf("\n test passed \n"); 
    } 
} 
+11

爲什麼不'a == b'? – iammilind 2012-04-02 10:24:00

+1

@iammilind:A和B可能是一些D的基類,但彼此無關 – user396672 2012-04-02 10:27:02

+2

@iammilind的+1 - 舊的'uns是最好的! – 2012-04-02 10:27:15

回答

1

我試圖通過比較這些指針指向的地址來解決這個問題。

  • 地址它指向基於指針類型的更改。

因此理論上可以說像

a *和b *指向同一個對象,如果存在一些對象C類型C的,使得兩個* a和* b是在類某處C.層次結構「

按道理

我們不得不重新審視上述聲明像 」 a *和b *指向同一個對象但它obj的ç的內存訪問自己的區域C型,使得兩個* a和* b是某處C的類層次結構 「」

struct Aa { int a; Aa() {a= 0;} };

struct Bb 
{ int b; 
    Bb() { b= 0;} 
}; 
struct C: Aa, Bb {  
}; 

C c; 
Aa *a1 = &c; 
Aa *a2 = &c; 
Bb *b1 = &c; 
Bb *b2 = &c; 

cout << &c << "\t"<< &(*a1)<<"\t"<< &(*a2)<<endl; 
cout << &c << "\t"<< &(*b1)<<"\t"<< &(*b2)<<endl; 

輸出:

  • &Ç0x0012fd04
  • &(* A1)0x0012fd04
  • &(* A2)0x0012fd04
  • &(* B1)0x0012fd08
  • &(* b2)的0x0012fd08

雖然這不能解決您的問題,但我們有一點要在此推斷。

1

這似乎對我來說是最臭的方式來處理,這是引入一個基類對於A & B:

#include <iostream> 

struct Base 
{ 
    virtual ~Base() {}; 
}; 

struct A : public virtual Base 
{ 
    int a; 
    virtual ~A() {}; 
    virtual void afunc() {}; 
}; 



struct B : public virtual Base 
{ 
    int b; 
    virtual ~B() {}; 
    virtual void bfunc() {}; 
}; 

struct C: A, B 
{}; 

int main() 
{ 
    C c; 
    A *a = &c; 
    B *b = &c; 

    std::cout << "a* == " << &(*a) << std::endl; 
    std::cout << "b* == " << &(*b) << std::endl; 
    std::cout << "a == b == " << ((void*)a == (void*)b) << std::endl; 

    Base* ba = a; 
    Base* bb = b; 

    std::cout << "ba* == " << &(*ba) << std::endl; 
    std::cout << "bb* == " << &(*bb) << std::endl; 
    std::cout << "ba == bb == " << (ba == bb) << std::endl; 

    return 0; 
} 
+0

我同意它是最不臭的,雖然,嚴格來說,它不是一個答案:)。 – user396672 2012-04-02 17:25:53

0

由於與dynamic_cast您還可以在類型層次投「杯酒人生」,我建議:

(b != nullptr? dynamic_cast<B*>(a) == b : a == nullptr) 

如果a點,在*b開始一些子對象,然後dynamic_cast<B*>(a)必然會返回一個空指針(因爲有沒有辦法B包含自身)。因此,如果b不是空指針,則dynamic_cast<B*>(a) == b只有在共享相同的大多數派生類時纔會成功。 b是一個空指針的情況下,必須特別處理,因爲如果a不爲空,但沒有指向從B派生的類,則dynamic_cast測試將失敗。

但也有涉及多重繼承哪裏該解決方案將提供一個假陰性(不像你的解決方案,它從來沒有給假陰性,但可產生假陽性)的一些情況。然而,這可能發生的類層次結構是層次結構,我說你不應該創建(即包含多個類型B的間接基地的派生類)。您可以通過交換角色ab再次測試來減少漏報次數(然後僅當均爲AB在最派生的類中不明確時,測試將失敗)。

您還可以結合你的和我的測試給出三種結果:

  • 這兩項測試成功:指針肯定是同一對象(或諾斯空)。
  • 兩個測試都失敗:指針絕對不是同一個對象。
  • 只有我的測試失敗:要麼您的測試給出了假陽性,我的測試給出了一個假陰性。你不能確定兩者是否是同一個對象,但至少你可以告訴你說不出來。
相關問題