是否有可能将一个对象转发给一个没有在C ++中定义额外变量或vtable的子类?

时间:2013-02-19 07:57:07

标签: c++ downcast

是否可以将对象向下转换为子类并不定义任何额外的变量或虚方法?

如果我有这些课程,

class A { public: A (); };
class B : public A { public: void method1 () {} B (); };

这是(1)可能和(2)标准安全吗?

A* a = new A ();
B* b = (B*)a;
b->method1();

4 个答案:

答案 0 :(得分:6)

指针转换充当static_cast。 5.2.9 / 2说,

  

如果[A]类型的对象实际上是对象的子对象   键入[B],结果引用类型为[B]的封闭对象。   否则,演员的结果是未定的。

您的对象B对象的子对象,因此结果未定义。

即使你是reinterpret_cast,通过结果指针访问对象的值也有未定义的行为,因为它违反了严格的别名。在C ++ 11标准中,3.10 / 10:

  

如果程序试图通过访问对象的存储值   行为是除以下类型之一以外的glvalue   理解过程音响定义:

“不添加任何数据成员或虚拟成员函数的对象的动态类型的派生类”在后面的列表中

根据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中,副本也许可以提高效率,但即便如此,我希望你们同意你必须跳过使用成员函数语法的箍是不值得的

答案 1 :(得分:4)

B * b = static_cast< B *> a或您尝试的是不安全的向下转换,它将基类对象(A)的地址分配给派生类(B)指针。因此,如果您通过该指针访问任何内容,则会导致未定义的行为。

假设A的实例有一个可能的对象布局:

a ->|vptr|

执行强制演员时:

b ->|vptr|

它肯定是不安全的,因为它没有指向B的实例或B的子类。当你调用虚方法或修改字段(不是在这种情况下)时,它通常会导致undefined behavior或此布局中的错误。

但是,您的method1是非虚拟的,因此无需查找虚拟表。由于你对method1的实现没有甚至不能对“this”做任何事情,所以当你在这个假设的对象布局中运行代码时,它将概率地报告没有错误(参见James的评论)。

答案 2 :(得分:3)

  1. 是的,有可能。
  2. 不,按标准不安全;气馁。
  3. 这更像是C void * cast,这在C ++中不是最好的选择。但我这样做了很多次并且工作正常。

答案 3 :(得分:1)

你应该阅读这篇写得非常好的帖子:

Regular cast vs. static_cast vs. dynamic_cast

如果你使用“static_cast”,请注意你是“强制”转换,这本质上是不安全的。