是否可以将对象向下转换为不定义任何额外变量或虚拟方法的子类?
如果我有这些课程,
class A { public: A (); };
class B : public A { public: void method1 () {} B (); };
这(1)可能和(2)按标准安全吗?
A* a = new A ();
B* b = (B*)a;
b->method1();
指针转换充当static_cast
. 5.2.9/2 说,
如果 [ ] 类型的对象
A
实际上是 [ ] 类型的对象的子对象B
,则结果引用 [B
] 类型的封闭对象。否则,强制转换的结果是不确定的。
您的对象不是对象的子B
对象,因此结果未定义。
即使您要reinterpret_cast
通过结果指针访问对象的值,也会有未定义的行为,因为它违反了严格的别名。在 C++11 标准 3.10/10 中:
如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义:
“不添加数据成员或虚拟成员函数的对象的动态类型的派生类”不在下面的列表中。
根据method1
实际所做的事情,有可能在调用对象时避免访问对象的存储值。但我不确定这是否可能。除非在标准的其他地方另有说明,否则我会假设调用非静态成员函数固有地“访问对象的存储值”,即使该函数实际上不使用任何数据成员也是如此。
这是可能在实践中一直或几乎一直有效的尴尬案例之一。但这并不能保证,所以即使它看起来可以工作并且发出的代码看起来没问题,你也会担心有一天新的优化会破坏它。
一旦定义,C++ 类就对新成员关闭,包括新成员函数。因此,您创建的对象new A()
具有它将拥有的所有成员函数。只需编写一个非成员函数——除非A
有protected
成员,否则它将具有与您的成员函数完全相同的访问权限A
。如果A
确实有protected
成员,那么就有一种经过批准的派生方式,您应该使用它来创建适当的B
.
如果成员函数语法对您来说意义重大,那么根据A
您可能能够编写的类:
B b = *a; // "copy" the object (give B a suitable ctor)
b.method1(); // act on it
*a = b; // copy it back (A needs copy assignment operator)
显然这里有一些问题可能会阻止它工作:首先是对象是否可复制,线程安全,以及是否method1
存储指向某个地方的指针/引用,b
一旦b
被破坏就会开始悬挂。在 C++11 中,副本可能是提高效率的举措,但即便如此,我希望您会同意,为了使用成员函数语法而必须跳过的圈子是不值得的。
B* b = static_cast< B* >a 或者您尝试的是不安全的向下转换,它将基类对象 (A) 的地址分配给派生类 (B) 指针。因此,如果您通过该指针访问任何内容,它将导致未定义的行为。
让我们假设 A 实例的一种可能的对象布局:
a ->|vptr|
当您执行强制施法时:
b ->|vptr|
这绝对是不安全的,因为 a 不指向 B 的实例或 B 的子类。当您调用虚拟方法或修改字段(在这种情况下不是)时,通常会导致未定义的行为或此布局中的错误。
但是,您的 method1 是非虚拟的,因此无需查找虚拟表。由于您对 method1 的实现没有甚至不能对“this”做任何事情,所以当您在这个假设的对象布局中运行代码时,它可能不会报告任何错误(参见 James 的评论)。
这更像是 C void* 强制转换,这不是 C++ 中最好的做法。但我这样做了很多次,效果很好。