简体   繁体   中英

C++ pointer multi-inheritance fun

I'm writing some code involving inheritance from a basic ref-counting pointer class; and some intricacies of C++ popped up. I've reduced it as follows:

Suppose I have:

class A{};
class B{};
class C: public A, public B {};

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

// does pa point to a valid A object?
// does pb point to a valid B object?

// does pa == pb ?

Furthermore, does:

// pc == (C*) pa ?
// pc == (C*) pb ?

Thanks!

  • does pa point to a valid A object?
  • does pb point to a valid B object?

Yes, the C* gets converted so that pa and pb point to the correct addresses.

  • does pa == pb ?

No, usually not. There can't be an A object and a B object at the same address.

Furthermore, does

  • pc == (C*) pa ?
  • pc == (C*) pb ?

The cast converts the pointers back to the address of the C object, so both equalities are true.

C embeds an A and a B .

class C: public A, public B {};

is very similar to the C code

struct C {
    A self_a;
    B self_b;
};

and (B*) &c; is equivalent to static_cast< B* >( &c ) is similar to &c.self_b if you were using straight C.

In general, you can't rely on pointers to different types being interchangeable or comparable.

pc == pa;
pc == pb;

Not defined, depends on class structure.

pc == (C*) pa;
pc == (C*) pb;

Thats ok.

pa == pb;

No.

Do they point to valid objects?

Yes

Item 28 Meaning of Pointer Comparison in C++ Common Knowledge: Essential Intermediate Programming ) explains the key of object pointer in C++:

In C++, an object can have multiple, valid addresses, and pointer comparison is not a question about addresses. It's a question about object identity.

Take a look at the code:

class A{};
class B{};
class C: public A, public B {};

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

class C derives from both class A and class B , so class C is both class A and class B . the object C c has 3 valid addresses: address for class A , class B and class C . The implementation depends on compiler, so you can't assume the memory layout of class C , and it may like this:

 ----------  <- pc (0x7ffe7d10e1e0)
 |        |
 ----------  <- pa (0x7ffe7d10e1e4)
 | A data |
 ----------  <- pb (0x7ffe7d10e1e8)
 | B data |
 ----------
 | C data |
 ----------

In above case, although the address value of pc , pa and pb aren't same, they all refer to the same object ( c ), so the compiler must ensure that pc compares equal to both pa and pb , ie, pc == pa and pc == pb . The compiler accomplishes this comparison by adjusting the value of one of the pointers being compared by the appropriate offset. Eg,

pc == pa

is translated to:

pc ? ((uintptr_t)pc + 4 == (uintptr_t)pa) : (pa == 0)

Among other things, since A and B have no inheritance relationship, we can't compare pa and pb directly.

For your questions:

(1) does pa point to a valid A object?  
(2) does pb point to a valid B object?  
Yes, refer the above diagram. 

(3) pc == (C*) pa ?  
(4) pc == (C*) pb ?  
Yes, No need to add (C*).

(5) does pa == pb ?
No. We can't compare them.

What you get is something like this in memory

 ----------
 | A data |
 ----------
 | B data |
 ----------
 | C data |
 ----------

So if you want the entire C object you'll get a pointer to the beginning of the memory. If you want only the A "part", you get the same address since that's where the data members are located. If you want the B "part" you get the beginning + sizeof(A) + sizeof(whatever the compiler adds for vtable). Thus, in the example, pc != pb (could be pc != pa) but pa is never equal to pb.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM