15

Consider two pointers

A* a; 
B* b;

Both A and B are polymorphic classes. How to check whether a and b point to the same object or not?

More precisely, let's specify a and b point to the same object if there exists some object d of type D such that both *a and *b are somewhere in the class hierarchy of d.

I would propose the following solution:

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

Indeed, according to the standard,

dynamic_cast<void*>(v) 

yields ”a pointer to the most derived object pointed to by v. (n3242.pdf: § 5.2.7 - 7). If the most derived for both is the same object, then the pointers point to the same object.

I'm pretty sure that it should always work correctly from the practical viewpoint. But theoretically, at first glance the proposed equality seems to produce false positive, for example, in case if b points to the first member of A (not to A's ancestor). Although it's practically impossible to get equal addresses for A and its member since A's virtual table pointer should be located before this member, the standard doesn't mandate virtual tables and says nothing about the class layout.

So, my questions are:

  1. Is the proposed solution correct from the standard viewpoint?

  2. Are there any caveats about private (protected) inheritance or cv-qualification ?

  3. Are there better solutions?

[EDIT]

I tried to present some example that illustrates a relatively complex scenario. In this case dynamic crosscasting and static casting are ambiguous.

 // 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");
  }
}
q-l-p
  • 4,304
  • 3
  • 16
  • 36
user396672
  • 3,106
  • 1
  • 21
  • 31
  • 12
    Why not `a == b` ? – iammilind Apr 02 '12 at 10:24
  • 1
    @iammilind: A and B may be base classes for some D but unrelated to each other – user396672 Apr 02 '12 at 10:27
  • 2
    +1 for @iammilind - the old 'uns are the best! – Martin James Apr 02 '12 at 10:27
  • @user396672, even if `a` and `b` are different pointers pointing to derived `D` ===> still `a == b` will be true if they point to the same object. – iammilind Apr 02 '12 at 10:29
  • 2
    @user396672: Since you seem to insist that `a == b` is insufficient for your case, I would be interested in seeing a simple self-compiling example program (in particular, the static types of the pointers and the class hierarchy) which illustrates the scenario more precisely than english words. – Frerich Raabe Apr 02 '12 at 10:33
  • @iammilind: a==b even produce a compiler error for urelated classes :) – user396672 Apr 02 '12 at 10:34
  • 1
    @user396672 That's right, why would you even want to compare pointers to different classes?. You could make it compile with some shady cast, but I fail to see a use case for that. – jrok Apr 02 '12 at 10:37
  • 1
    Comparing unrelated type pointers is considered **code smell**. Should be avoided. – iammilind Apr 02 '12 at 10:39
  • @jrok: For instance, one may have to pointers to some two interfaces and she(he)is interested whether the two interfaces actually repesent the same object. Although this question rather continues a discussion under this answer: http://stackoverflow.com/questions/9943264/when-can-i-compare-pointers-to-the-same-object-in-c/9943768#9943768 since Konrad's point was not clear to me – user396672 Apr 02 '12 at 10:46
  • @iammilind: dynamic_cast should be avoided too. However, it presents in the standard – user396672 Apr 02 '12 at 10:51
  • @iammilind: You said, "even if a and b are different pointers pointing to derived D ===> still a == b will be true if they point to the same object". This is false. Firstly, `a == b` doesn't compile if `A` and `B` are unrelated bases of some derived class `D`. Secondly, since `D` uses multiple inheritance, the address of its `A` base class sub-object need not be equal to the address of its `B` base class sub-object. So they can point to different sub-objects of the same object, and be non-equal. This is one of the situtations the questioner wants to detect. – Steve Jessop Apr 02 '12 at 11:05
  • 13
    @Frerich: consider `struct A { int a; virtual ~A(); }; struct B { int b; virtual ~B(); }; struct C: A, B {}; int main() { C c; A *a = &c; B *b = &c; }`. Now, `a` and `b` are not comparable, but they do refer to different bases of the same object. `(void*)a != (void*)b`, but `dynamic_cast(a) == dynamic_cast(b)`. That's the different between pointer comparison and the thing the questioner is asking about. – Steve Jessop Apr 02 '12 at 11:10
  • 1
    @SteveJessop: Thanks, I guess it would have saved a lot of comments if that example had been included in the original question. – Frerich Raabe Apr 02 '12 at 11:16
  • @SteveJessop, I got that point when in the later comment questioner mentioned the example of "unrelated classes". I replied that, mostly it should be avoided. Because I don't see much of use case for such situation. – iammilind Apr 02 '12 at 11:22
  • @Frerich: I tried to avoid any particular examples as I'd want to present the problem in its most general form. Perhaps I was wrong and a few examples would clear up the matter. Sorry if so. Now it seems redundant (after Steve's nice example) – user396672 Apr 02 '12 at 16:34
  • Why not override operator== in your class and then do a field-by-field comparison so that when you want to compare if the pointers point to the same thing, just de-reference them and compare. –  Apr 02 '12 at 17:19

3 Answers3

2

It would seem to me the least smelly way to deal with this is to introduce a base class for 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;
}
Douglas Leeder
  • 52,368
  • 9
  • 94
  • 137
1

I was trying to resolve this by comparing the address these pointers pointing to.

  • Address it is pointing to changes based on the type of pointer.

Hence theoretically we can say like

a* and b* points to the same object if there exists some object c of type C such that both *a and *b are somewhere in the class hierarchy of C."

Logically

we have to revisit the above statement like "a* and b* points to the same object but has it own zone of access in the memory of obj c of type C such that both *a and *b are somewhere in the class hierarchy of 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;

Output:

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

Though this will not solve your problem, we have a point to infer here.

Akaanthan Ccoder
  • 2,159
  • 5
  • 21
  • 37
0

Since with dynamic_cast you also can cast "sideways" in the type hierarchy, I would suggest:

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

If a points to some subobject at the beginning of *b, then dynamic_cast<B*>(a) will necessarily return a null pointer (because there's no way a B contains itself). Therefore if b is not a null pointer, dynamic_cast<B*>(a) == b will succeed only if both share the same most derived class. The case that b is a null pointer has to be treated specifically because if a is not null, but does not point to a class derived from B, the dynamic_cast test will fail.

However there are some situations involving multiple inheritance where this solution will give a false negative (unlike your solution which never gives false negatives, but can give false positives). However the class hierarchies where this might happen are hierarchies which I'd say you shouldn't create anyway (namely the same derived class containing multiple indirect bases of type B). You can reduce the number of false negatives by testing again through exchanging the role of a and b (then only if both A and B are ambiguous in the most derived class, the test will fail).

You could also combine your and my test to give three results:

  • Both tests succeed: The pointers definitely are to the same object (or noth null).
  • Both tests fail: The pointers definitely are not to the same object.
  • Only my test fails: Either your test has given a false positive, or my test has given a false negative. You can't tell for sure whether both are the same object, but at least you can tell that you can't tell.
celtschk
  • 19,311
  • 3
  • 39
  • 64