如何检查两个指针​​是否指向同一个对象?

时间:2012-04-02 10:17:55

标签: c++ dynamic-cast

考虑两个指针

A* a; 
B* b;

A和B都是多态类。 如何检查a和b是否指向同一个对象?

更确切地说,如果存在类型D的某个对象d,则指定a和b指向同一对象,使得* a和* 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. 有更好的解决方案吗?

  4. [编辑]

    我尝试提供一些示例来说明相对复杂的情况。在这种情况下,动态交叉铸造和静态铸造是模糊的。

     // 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");
      }
    }
    

3 个答案:

答案 0 :(得分:2)

在我看来,解决这个问题最不臭的方法是为A&amp; amp;引入一个基类。 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;
}

答案 1 :(得分:1)

我试图通过比较这些指针指向的地址来解决这个问题。

  • 地址指向基于指针类型的更改。

因此理论上我们可以说像

  

a *和b *指向同一个对象,如果存在某个类型为C的对象c,则* a和* b都位于C的类层次结构中。“

逻辑

  

我们必须重新审视上述声明   “a *和b *指向同一个对象,但在类型C的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;

输出:

  • &amp; c 0x0012fd04
  • &amp;(* a1)0x0012fd04
  • &amp;(* a2)0x0012fd04
  • &amp;(* b1)0x0012fd08
  • &amp;(* b2)0x0012fd08

虽然这不能解决您的问题,但我们有一点可以在这里推断。

答案 2 :(得分: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不为null,但未指向派生自B的类,则dynamic_cast测试将失败。< / p>

然而,有些情况涉及多重继承,其中此解决方案会给出假阴性(与您的解决方案不同,它永远不会产生假阴性,但会产生误报)。然而,可能发生这种情况的类层次结构是我不应该创建的层次结构(即包含多个B类型的间接基础的相同派生类)。您可以通过交换ab的角色再次进行测试来减少漏报的数量(然后仅当 AB在派生类最多的时候,测试会失败)。

您还可以将您和我的测试结合起来,得出三个结果:

  • 两个测试都成功:指针肯定是同一个对象(或者noth null)。
  • 两个测试都失败了:指针肯定不是同一个对象。
  • 只有我的测试失败:要么你的测试给出了误报,我的测试给出了假阴性。你无法确定两者是否是同一个对象,但至少你可以告诉你无法分辨。