我有一个来自两个父母的C子类.我创建了该类的一个对象,然后将它的引用转换为它的两个父类.转换值不相同,但仍保留子引用的值.这是代码:
class Mom { public: int a = 1; }; class Dad { public: int b = 2; }; class Child : public Mom,public Dad { public: int c = 3; }; int main() { Child* c = new Child; // 0x00C5A618 Mom* m = c; // 0x00C5A618 Dad* d = c; // 0x00C5A61C return 0; }
现在,我猜测通过应用static_cast<>获得的地址是那些实际的子对象.碰巧,妈妈是第一个子对象,所以它实际上与Child有相同的地址.爸爸是第二个子对象,因此它位于不同的地址.
据我所知,C风格的演员阵容,就像这样
Mom* m = (Mom*)c; Dad* d = (Dad*)c;
将应用static_cast<>什么时候这样做是合法的.实际上,当我实际编译并执行该版本时,我会看到与以前相同的行为.
继续这个闪烁的传奇,使用隐式演员,像这样
Mom* m = c; Dad* d = c;
也表现出相同的行为,我从中推断隐式强制转换也适用于static_cast<>当等效的显式演员合法时.
向后转回Child也做同样的事情:
Child* k = static_cast<Child*>(d); // 0x00C5A618
k中的值与d中的值不同,已经被设置回c中最初的值.
我猜测,在这种情况下,有一个运行时检查,看看是否确实d指向Child对象的Dad子对象.
到目前为止,这一切都是有道理的,但我很惊讶,对于单独派生的层次结构,没有这样的地址调整(如果这是正确的调用它).也就是说,如果我的层次结构只是Mom-> Dad-> Child,则将对Child的引用转换为指向Mom或Dad的指针永远不会更改值:
class Mom { public: int a = 1; }; class Dad : public Mom { public: int b = 2; }; class Child : public Dad { public: int c = 3; }; int main() { Child* c = new Child; // 0x00C5A618 Mom* m = c; // 0x00C5A618 Dad* d = c; // 0x00C5A618 Child* k = static_cast<Child*>(d); // 0x00C5A618 return 0; }
我希望它可以通过每个附加子类所需的存储大小或四个字节(int的大小)来改变.但是,他们没有.上例中的所有指针c,m,d和k具有相同的值.
所以,现在我猜测对象是在内存中布局的,Mom对象位于最低地址,爸爸对象下一个所需的额外空间,以及Child最后需要的额外空间.既然爸爸是妈妈,爸爸的地址和妈妈的地址一样,不是吗?
同样,一个孩子也是一个妈妈,所以它的地址也和它自己的妈妈子对象的地址一样.
所以…
我认为它的工作原理如下:在内存中,对象相互跟随,在纯粹单独派生的层次结构中,向上转换和向下转换总是产生相同的地址,而在多个派生的层次结构中,一些向上转换产生不同的地址,因为不是所有子对象将从派生对象的相同地址开始.像这样:
Possible Memory Layout for a Singly Derived Hierarchy +-------+-----+-----+---+ | Child : Dad : Mom : a | | | +-----+---+ | | : b | | +-----------+---+ | : c | +-----------------------+ Possible Memory Layout for a Multiply Derived Hierarchy +-------+-----+---+ | Child : Mom : a | | +-----+---+ | : Dad : b | | +-----+---+ | : c | +-----------------+
对不起,我有点絮絮叨叨,但我的问题是这样的事实:对于一个乘法派生对象的某些基类的向上转换会改变引用的值,这是事后转换后的值是地址的结果内存中的基础对象?
解决方法
在多重继承的情况下,所有父节点都不可能占用相同的内存(让我们忽略可能的空基优化),因此所有但只有一个基类地址必须与子地址不同.