开发者

C++ pointer multi-inheritance fun

开发者 https://www.devze.com 2022-12-18 14:16 出处:网络
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:

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.


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, i.e., 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. E.g.,

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.


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消