为什么将子指针向上转换为基类有时会更改指针值
Why does upcasting a child pointer to a base class sometimes change the pointer value?
我发现了其他一些似乎与我的问题有关的问题,但没有一个直接回答它(即使是几乎同名的问题)。
我有一个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;
}
我本来希望它会根据每个额外子类所需的存储大小或四个字节(整数的大小)来改变。但是,他们没有。上面示例中的所有指针,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 |
+-----------------+
抱歉,我漫无边际,但我的问题是:向上转换为乘法派生对象的某些基类会更改引用的值这一事实是否是由于后转换值是内存中基对象的地址
是的,你是对的。向上转换指针会给出存储在子类内部某处的父子对象的地址。通常,父子对象存储在子对象的开头,因此指向子对象和基子对象的指针是相同的。
在多重继承的情况下,所有父类地址都不可能占用相同的内存(让我们忽略可能的空基优化),因此除了一个基类地址之外,所有基类地址都必须与子地址不同。
我猜,在这种情况下,有一个运行时检查,以查看
d
是否确实指向Child
对象的Dad
子对象。
否,如果要进行运行时检查,请使用 dynamic_cast
(但源类型必须是多态的)。使用static_cast
编译器将只假设类型是正确的,否则会发生未定义的行为。
我认为它是这样工作的:在内存中,对象相互跟随,因此,在纯粹单独派生的层次结构中,向上转换和向下转换总是产生相同的地址
这不能保证;标准没有指定。编译器不必布置基类和成员子对象,以便基类子对象排在第一位,尽管在这些情况下编译器似乎这样做;但可能还有其他地址可能不同的情况,例如,如果派生类包含虚函数而基类不包含。 (在这种情况下,布局可能首先是 vptr,然后是基类子对象,然后是成员子对象。
向上转换为乘法派生对象的某些基类会更改引用的值,这是否是由于转换后的值是内存中基对象的地址这一事实的结果?
是的。
- 如何使用基类指针引用派生类成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如果基类包含双指针成员,则派生类的构造函数
- 使用基类指针调用基类的值构造函数的语法是什么?
- 如何在基类指针向量的元素上应用重载的多态函数
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- 指向基类派生类的 std::unique_ptr 的指针
- 当目标指针不是基类的类型时,为什么允许dynamic_cast为多态类生成 null 指针?
- 如何在成为指向基类的指针后保留对子类方法的使用?
- 派生类是从基类继承 v 指针并仅使用它,还是也有自己的 v 指针?
- std::variant vs指向C++中异构容器基类的指针
- 模板化函数或具有指向基类的指针的函数
- 指向基类的唯一指针
- C++函数解析基类指针到派生类指针
- 指向基类的指针的 std::vector 的深层副本
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 无法从指针派生类转换为指针基类(多态性)
- 使用指针基类的=操作符的多态性