将指向多态基类对象的指针显式转换为派生类指针并调用派生类函数是否属于ill-formed或未定义行为?
咱们先把核心结论摆出来:这种写法确实属于未定义行为(Undefined Behavior, UB),哪怕你的Derived类没有新增任何数据成员,只有新函数,也改变不了这个事实。
下面咱们一步步拆解为什么会这样:
C风格强制转换在这里等价于
static_cast
你自己的理解完全正确:这里的(Derived*)&b在C里会被解析为static_cast<Derived*>(&b)。而C标准对static_cast的向下转换(基类转派生类)有明确规定:如果被转换的指针/引用,实际指向的对象并不是目标派生类对象的基类子对象,那么行为就是未定义的。
回到你的代码:b是一个独立的Base对象,它从来不是任何Derived对象的一部分——只有Derived对象会包含一个Base子对象,反过来Base对象不会是Derived的子对象。所以这个转换直接触发了标准里明确的未定义行为。为什么在线编译器能正常运行?只是巧合而已
未定义行为最坑人的地方就是:它可能看起来完全正常,但这纯粹是编译器实现的巧合。在你的例子里,Derived没有新增数据成员,new_function只是访问基类的data成员——编译器生成的代码会直接用this指针去取对应偏移量的成员,而因为单继承且无额外数据,Base和Derived的指针地址在这个编译器里刚好是同一个,所以能正确拿到data的值。但这绝对不是标准保证的,换个编译器、换个编译选项,甚至只是给Derived加个无关的成员变量,结果可能就变成崩溃、拿到垃圾值,或者程序行为完全不可控。关于VS2010的特定行为
老版本的VC++(比如VS2010)在处理这种场景时,表现和你测试的在线编译器类似:因为单继承且Derived无额外数据,指针不需要做地址偏移,调用new_function时this指针指向的还是原来的Base对象,而函数里访问的data是基类成员,所以能正常运行。但同样,这只是VC的特定实现细节,不是C标准的规定,绝对不能依赖这种行为写代码。为什么
dynamic_cast会失败?这才是正确的表现
你提到dynamic_cast会返回nullptr(指针转换)或抛出异常(引用转换),这完全符合标准:dynamic_cast会检查对象的实际动态类型,这里对象是纯粹的Base,所以转换为Derived*必然失败。这也是为什么我们推荐在需要向下转换时用dynamic_cast——它是唯一能安全检查转换合法性的方式。
最后给你个小建议:如果真的需要在类似场景下调用Derived的函数,正确的做法要么是确保对象确实是Derived类型(比如用dynamic_cast先检查),要么把函数设计成基类的虚函数,或者把new_function改成非成员函数,接收Base&作为参数来访问data。
内容来源于stack exchange

