12

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

如果我有这些课程,

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

4 回答 4

7

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

如果 [ ] 类型的对象A实际上是 [ ] 类型的对象的子对象B,则结果引用 [ B] 类型的封闭对象。否则,强制转换的结果是不确定的。

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

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

如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义:

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

根据method1实际所做的事情,有可能在调用对象时避免访问对象的存储值。但我不确定这是否可能。除非在标准的其他地方另有说明,否则我会假设调用非静态成员函数固有地“访问对象的存储值”,即使该函数实际上不使用任何数据成员也是如此。

这是可能在实践中一直或几乎一直有效的尴尬案例之一。但这并不能保证,所以即使它看起来可以工作并且发出的代码看起来没问题,你也会担心有一天新的优化会破坏它。

一旦定义,C++ 类就对新成员关闭,包括新成员函数。因此,您创建的对象new A()具有它将拥有的所有成员函数。只需编写一个非成员函数——除非Aprotected成员,否则它将具有与您的成员函数完全相同的访问权限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 中,副本可能是提高效率的举措,但即便如此,我希望您会同意,为了使用成员函数语法而必须跳过的圈子是不值得的

于 2013-02-19T09:46:17.847 回答
4

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

让我们假设 A 实例的一种可能的对象布局:

a ->|vptr|

当您执行强制施法时:

b ->|vptr|

这绝对是不安全的,因为 a 不指向 B 的实例或 B 的子类。当您调用虚拟方法或修改字段(在这种情况下不是)时,通常会导致未定义的行为或此布局中的错误。

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

于 2013-02-19T08:02:50.000 回答
4
  1. 是的,有可能。
  2. 不,按照标准是不安全的;这是不鼓励的。

这更像是 C void* 强制转换,这不是 C++ 中最好的做法。但我这样做了很多次,效果很好。

于 2013-02-19T10:18:41.093 回答
1

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

常规演员与 static_cast 与 dynamic_cast

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

于 2013-02-19T08:02:30.133 回答