C++ Multiple Inheritance Memory Addressing issue

℡╲_俬逩灬. 提交于 2019-12-23 20:08:25

问题


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?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/10056790/c-multiple-inheritance-memory-addressing-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!