C++ C++;多重继承内存寻址问题

C++ C++;多重继承内存寻址问题,c++,inheritance,mingw32,C++,Inheritance,Mingw32,请忽略#include部分,假设它们正确完成。此外,这可能是特定于实现的(但vtables的概念也是如此),但我很好奇,因为它增强了我对多重继承的可视化能力。(顺便说一下,我正在使用MingW4.4.0) 初始代码: class A { public: A() : a(0) {} int a; }; //Edit: adding this definition instead void f(void* ptrA) { std::cout<<((A*)ptrA)-&

请忽略#include部分,假设它们正确完成。此外,这可能是特定于实现的(但vtables的概念也是如此),但我很好奇,因为它增强了我对多重继承的可视化能力。(顺便说一下,我正在使用MingW4.4.0)

初始代码:

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
objC的内存模型:

//<1> stuff from B
//<2> stuff from B
//<3> stuff from A : int a
//<4> stuff from C : int c
//来自B的东西
//B的东西
//来自A的东西:int A
//来自C:intc的东西
&objC
将包含上述假定的内存模型的起始地址 编译器将如何/何时将其转换到?是否在检查标签1处的呼叫时发生

编辑::


因为标签1似乎是一个赠品,只是让编译器对它更加模糊。请看上面编辑的代码。现在,编译器什么时候做,在哪里?

简短回答:如果编译器知道基类和派生类之间的关系,它将在强制转换操作期间调整指针值

假设类C的对象实例的地址是地址100。假设sizeof(C)==4。sizeof(B)和sizeof(A)也是如此

当发生以下情况时:

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
编译器将负责在分配期间调整指针值

所有这一切的一个“陷阱”是当一个类在没有完整声明的情况下被向前声明时:

 // 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
 }
那真是一只虫子


我的一般规则。避免使用旧的“C风格”强制转换,支持使用static_cast操作符,或者只依赖隐式强制转换,而不使用操作符来执行正确的操作(对于向上强制转换)。如果强制转换无效,编译器将发出错误。

是的,您完全正确

要完全理解这种情况,您必须了解编译器在两点上的知识:

  • 标签1处(如您已经确定的)
  • 内部函数f()

    (1) 编译器知道C和A的确切二进制布局,以及如何从C*转换为A*,并将在调用站点(标签1)进行转换

    (2) 但是,在函数f()中,编译器只需要知道A*,因此只限于A的成员(在本例中为int A),不能混淆特定实例是否是其他实例的一部分


  • B级应该看起来像A级吗?也许你也可以添加它的代码。“这的确是个很有趣的问题。”哈茨基:B类可以是任何东西。您可以自由地在内存模型中插入任何数据。为了简单起见,假设任何地方都没有虚拟函数和虚拟继承。但是,为什么编译器不进行延迟编译,直到它找到一个编译单元来消除这种特殊情况下的歧义?您的答案的第一部分对我编辑的代码是否仍然有效?(现在是否遍历了目标代码中的完整f(…)定义?)。在不存在继承和多重继承的C中,结构类型之间的盲转换(如(A*)&C)是完全有效的,并且经常使用。因此,C++必须处理这种遗留代码,但后来发明了STATICE-CAST运算符,使事情更安全。在编辑的代码中,因为在调用函数F时,您正在铸造派生类实例指针(不精确地)以无效*,所以返回到(A*)是不安全的。指针值不会引起偏移量转换。你基本上做了一个“重新解释演员”的行为,这相当于说“ptrA=(a*)((void*)ptrC);”这与说“ptrA=ptrC”不同。因此,如果有人用表示从a派生的对象的参数调用“f”,但使用多重继承,那么就有可能出现bug.hmm对legacy的支持——这是通常的痛苦;但是谢谢你的回答。嗯,是的,世界上所有的不同。您刚刚删除了编译器执行正确操作所需的线索。这可能是该计划将深入研究的部分B。酷!因此,它会把编译器搞得一团糟,甚至不会做任何非法/未定义或欺骗的事情(比如类型转换
    this
    指向非常量类型的指针和修改const成员函数中的成员数据):)而且可能会有更多带有虚拟(函数和基)的蠕虫!!
    C* pC = (C*)(&a);
    
     // 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
     }