简体   繁体   中英

C++ Multiple Inheritance Memory Addressing issue

Please ignore the #include parts assuming they are done correctly. Also this could be implementation specific (but so is the concept of vtables) but i am just curious as it enhances me to visualize multiple inheritance. (I'm using MinGW 4.4.0 by the way)

initial code:

class A {
public:
   A() : a(0) {}
   int a;
};

//Edit: adding this definition instead
void f(void* ptrA) {
   std::cout<<((A*)ptrA)->a;
}
//end of editing of original posted code

#if 0
//this was originally posted. Edited and replaced by the above f() definition
void f(A* ptrA) {
   std::cout<<ptrA->a;
}
#endif

this is compiled and Object code is generated.

in some other compilation unit i use (after inclusion of header file for above code):

class C : public B , public A {
public:
   int c;
}objC;

f(&objC); // ################## Label 1

memory model for objC:

//<1> stuff from B
//<2> stuff from B
//<3> stuff from A : int a
//<4> stuff from C : int c

&objC will contain starting address of <1> in memory model assumed above how/when will the compiler shift it to <3>? Does it happen during the inspection of call at Label 1 ?

EDIT::

since Lable 1 seems to be a give away, just making it a little more obscure for the compiler. Pls see the Edited code above. Now when does the compiler do and where?

Short answer: Compiler will adjust pointer values during cast operations if it knows the relationship between the base and derived class.

Let's say the address of your object instance of class C was at address 100. And let's say sizeof(C) == 4. As does sizeof(B) and sizeof(A).

When a cast happens such as the following:

C c;
A* pA = &c;  // implicit cast, preferred for upcasting
A* pA = (A*)&c; // explicit cast old style
A* pA = static_cast<A*>(&c); // static-cast, even better

The pointer value of pA will be the memory address of c plus the offset from where "A" begins in C. In this case, pA will reference memory address 104 assuming sizeof(B) is also 4.

All of this holds true for passing a derived class pointer into a function expecting a base class pointer. The implicit cast will occur as does the pointer offset adjustment.

Likewise, for downcasting:

C* pC = (C*)(&a);

The compiler will take care of adjusting the pointer value during the assigment.

The one "gotcha" to all of this is when a class is forward declared without a full declaration:

 // foo.h
 class A;  // same as above, base class for C
 class C;  // same as above, derived class from A and B

 inline void foo(C* pC)
 {
      A* pA = (A*)pC; // oops, compiler doesn't know that C derives from A.  It won't adjust the pointer value during assigment
      SomeOtherFunction(pA); // bug! Function expecting A* parameter is getting garbage
 }

That's a real bug!

My general rule. Avoid the old "C-style" cast and favor using the static_cast operator or just rely on implicit casting without an operator to do the right thing (for upcasts). The compiler will issue an error if the casting isn't valid.

Yes, you are quite correct.

To fully understand the situation, you have to know what the compiler knows at two points:

  1. At Label 1 (as you have already identified)
  2. Inside function f()

    (1) The compiler knows the exact binary layout of both C and A and how to convert from C* to A* and will do so at the call site (Label 1)

    (2) Inside function f(), however, the compiler only (needs to) know(s) about A* and so restricts itself to members of A (int a in this case) and cannot be confused about whether the particular instance is part of anything else or not.

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