【发布时间】:2016-11-12 19:38:36
【问题描述】:
我发现了一些似乎与我的问题有关的其他问题,但没有人直接回答它(甚至 the one 几乎同名)。
我有一个从两个父母派生的 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 获得的地址是实际子对象的地址。碰巧,Mom 是第一个子对象,因此它实际上与Child 具有相同的地址。 Dad 是第二个子对象,所以它位于不同的地址。
据我了解,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 对象位于最低地址,Dad 对象接下来需要的额外空间,以及额外的空间需要Child 最后出现。由于Dad 是Mom,Dad 地址将与Mom 地址相同,不是吗?
同样,Child 也是一个Mom,因此它的地址也将与它自己的Mom 子对象的地址相同。
所以...
我认为它的工作原理是这样的:在内存中,对象彼此跟随,因此,在纯粹的单一派生层次结构中,向上转换和向下转换总是产生相同的地址,而在多重派生层次结构中,一些向上转换产生不同的地址,因为并非所有子对象都将从与派生对象相同的地址开始。像这样:
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 |
+-----------------+
抱歉,我有点啰嗦,但我的问题是:向上转换到乘法派生对象的某些基类会改变引用的值,这是因为后转换值基对象在内存中的地址是?
【问题讨论】: