向下转换基类型
Downcasting a base type
在C++中,如果基类对象实例化为基对象,随后向下转换为派生对象,是否为未定义行为?
当然,我认为它肯定必须是未定义的行为,因为派生类对象可能具有基类没有的成员变量。 因此,如果类被实例化为基对象,这些变量实际上将不存在,这意味着通过派生类指针访问它们必须导致未定义行为。
但是,如果 Derived 类只提供额外的成员函数,但不包含任何进一步的成员数据,该怎么办? 例如:
class Base
{
public:
int x;
};
class Derived : public Base
{
public:
void foo();
};
int main()
{
Base b;
Derived* d = static_cast<Derived*>(&b);
d->foo(); // <--- Is this undefined behavior?
}
此程序是否会导致未定义的行为?
是的,它仍然是未定义的行为,因为您在真正的d
类型上向编译器撒谎。
请参阅标准 5.2.9/8:
类型为"指向 cv1 B 的指针"的右值, 其中 B 是类类型,可以是 转换为类型的右值 "指向 cv2 D 的指针",其中 D 是一个类 从B派生(第10条),如果有效 从"指针到 D"到"指向 B"的指针存在 (4.10), CV2 与 CV资格相同,或 CV资格高于CV1,以及 B 不是 D 的虚拟基类。 空指针值 (4.10) 为 转换为空指针值 目标类型。如果右值 类型"指向 cv1 B 的指针"指向 B 这实际上是 类型 D 的对象,结果 指针指向封闭对象 类型为 D。否则,结果为 强制转换未定义。
最后两句话说,如果指针指向的B
实际上不是D
派生类的一部分,则强制转换是未定义的行为。
C++03 标准第 5.2.9.8 段对此进行了阐述(强调我的):
类型为"指向 cv1 B 的指针"的右值, 其中 B 是类类型,可以是 转换为类型的右值 "指向 cv2 D 的指针",其中 D 是一个类 从B派生(第10条),如果有效 从"指针到 D"到"指向 B"的指针存在 (4.10), CV2 与 CV 资格相同, 或比CV1更高的CV资格, 和 B 不是 的虚拟基类 D.空指针值 (4.10) 为 转换为空指针值 目标类型。如果右值 类型"指向 cv1 B 的指针"指向 B 这实际上是 类型 D 的对象,结果 指针指向封闭对象 类型为 D。否则,结果为 强制转换未定义。
是的,这是完全未定义的行为。这就是为什么在贬低时,你应该偏袒dynamic_cast
,除非你非常非常确定。
鉴于我对生成的机器代码C++实现的心智模型,我会说,如果调用的方法不是虚拟的,并且派生类没有引入虚拟方法,而基类没有,并且多重继承不涉及这种诡计和......如果方法代码确实只访问 Base 对象中定义的成员,它应该按预期工作。
然而,这在C++中仍然很明显是UB。
- 是否可以初始化不可复制类型的成员变量(或基类)
- 当基类是依赖类型时,这是一个缺陷吗
- 如何允许模板参数中的类类型,仅当它有两个基类时
- 如何使用 std::make_shared 创建基类类型的智能指针?
- 当目标指针不是基类的类型时,为什么允许dynamic_cast为多态类生成 null 指针?
- 为什么允许dynamic_cast到非唯一的基类类型?
- 在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数
- 我们可以在不知道其真实类型的情况下将基类指针转换为派生类指针吗?
- 从基类指针派生派生类的模板类型
- 基类类型向量中的派生结构
- 从基类的唯一指针中声明派生类的类型
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 如何隐藏模板基类的参数类型
- 即使基类和派生类只使用基元数据类型,我是否需要定义虚拟析构函数
- 添加字符串类型的类成员会导致调用基类函数而不是子函数
- C++ 模板:重载时找不到基类类型参数方法
- 如何从包含基类指针的容器中调用派生类函数(基于其类型)?
- 为什么嵌套类型的基类不需要"typename"?
- 将空基类优化对象强制转换为另一种类型是否会破坏严格的别名?
- 从具有泛型返回类型的 crtp 基类调用派生类中的函数