C++继承中的向下转型

29

我有一个基类,如下所示:

class point    //concrete class
{
 ...    //implementation
}

class subpoint : public point  //concrete class
{
...     //implementation
}

我如何从一个点对象转换为子点对象?我已经尝试了以下三种方法:

point a;
subpoint* b = dynamic_cast<subpoint*>(&a);
subpoint* b = (subpoint*)a;
subpoint b = (subpoint)a;

这些转换有何问题?

7个回答

45
我如何将一个点对象转换为子点对象?
除非“point”有一个转换运算符或“subpoint”有一个转换构造函数,否则你不能;在这种情况下,对象类型可以被转换而无需进行强制转换。
如果引用对象确实是类型为“subpoint”的话,你可以从“point”引用(或指针)强制转换为“subpoint”引用(或指针)。
subpoint s;

point & a = s;
subpoint & b1 = static_cast<subpoint&>(a);
subpoint & b2 = dynamic_cast<subpoint&>(a);

第一个方法(static_cast)较为危险;它没有检查转换是否有效,因此如果 a 不引用 subpoint,那么使用 b1 将具有未定义的行为。

第二个方法(dynamic_cast)更安全,但仅在 point 是多态的情况下才有效(即,如果它具有虚函数)。如果 a 引用不兼容类型的对象,则会抛出异常。


将要转换的对象实际上是子点类型,只是在点数组中。 - CodeKingPlusPlus
3
static_cast 也可能导致非对齐对象。我在32位ARM上遇到了这个问题。我将其转换为派生对象,然后调用仅存在于派生对象中的函数。UBsan产生了一堆发现。我不得不切换到 dynamic_cast 来解决这些问题。 - jww
@jww,如果我没有virtual函数,那么我是否仍然需要担心不对齐的对象,因为我根本无法使用dynamic_cast - user1032677

10

动态转换的目的是“在运行时检查对象是否属于层次结构中的某种类型”。 现在让我们看看您拥有什么:

  1. 您拥有一个点对象,而不是子点。
  2. 您正在询问动态转换对象是否为子点。 它不是。
  3. 因为它不是子点,所以动态转换失败 - 它告诉您该对象不是您尝试转换为的类型。

相比之下,这将起作用:

subpoint c;
point *a = &c;
subpoint* b = dynamic_cast<subpoint*>(&a);
subpoint* b = (subpoint*)a;

4
那是正确的答案!如果基类单独构造,那么它就不能被向下转型。也就是说,dynamic_cast会失败! - metablaster

6
总的来说,这样做行不通,因为point不是subpoint,只有反过来才是正确的。然而,还存在其他问题。
按顺序排列如下:
subpoint* b = dynamic_cast<subpoint*>(&a);

dynamic_cast 只对多态类型有效,也就是说,只有声明了至少一个虚函数的类型才能进行 dynamic_cast。我猜测 point 没有虚函数,因此不能与 dynamic_cast 一起使用。


subpoint* b = (subpoint*)a;

为了使此代码能够正常运行,point需要声明一个subpoint *的转换操作符,例如:point::operator subpoint *()


subpoint b = (subpoint)a;

为了使此转换生效,point需要声明一个转换运算符到subpoint 或者 subpoint需要有一个构造函数,该构造函数接受一个可从point转换的参数。

5
对于第一个例子,只有在基类中至少有一个虚方法时,dynamic_cast才能起作用。如果对象实际上不是您尝试转换的类型,则结果将为NULL。
对于第二个例子,您需要使用&a而不是a,但是一旦您解决了这个问题,由于对象类型错误,您将得到未定义的行为。
第三个例子需要在point中添加operator subpoint()方法以进行转换并创建副本。

1
@MooingDuck,"won't work" 是什么意思?它应该返回一个 NULL 指针,这可能不是预期的结果,但它确实可以编译并执行一些有用的操作。 - Mark Ransom

1

a 无法转换为 subpoint,该实现不可用。


1
这些强制转换有什么问题吗?
问题在于你尝试去做它们。一个“点”不是一个“子点”,如果它能工作,我会感到惊讶的。

0

我知道这是一个老问题(将近十年的问题),但最近我也遇到了同样的问题。

我不确定原帖作者的意图是什么,但有一种情况是你想要有一个基类,从中派生出子类,这些子类只提供不同于彼此的方法,并利用相同的基础数据,而不添加任何自己的数据元素。

如果你需要这样做(出于某种原因),并且想要能够将一个最初作为父对象实例化的子对象向下转换为子对象,则不要使用虚方法,因为它们会对你产生负面影响,你将始终被迫使用实例化的类的虚方法,而无论如何转换都不会调用子方法。

所以,这当然可以做到。只是不被认为是“安全”的。此外,你不能在这里使用dynamic_cast,必须使用static_cast


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接