是否可以将对象向下转换为子类并不定义任何额外的变量或虚方法?
如果我有这些课程,
class A { public: A (); };
class B : public A { public: void method1 () {} B (); };
这是(1)可能和(2)标准安全吗?
A* a = new A ();
B* b = (B*)a;
b->method1();
答案 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)
这更像是C void * cast,这在C ++中不是最好的选择。但我这样做了很多次并且工作正常。
答案 3 :(得分:1)
你应该阅读这篇写得非常好的帖子:
Regular cast vs. static_cast vs. dynamic_cast
如果你使用“static_cast”,请注意你是“强制”转换,这本质上是不安全的。