10

我在 C++ 中有以下情况:

  • 抽象基类Abstract1Abstract2. 他们是无关的。
  • Foo派生自Abstract1和的类Abstract2

我在一个编译单元中,我没有关于类的信息Foo(没有声明,没有定义)。只有Abstract1Abstract2是已知的。(实际上,Foo 甚至被定义在一个 DLL 中)

dynamic_cast 是否允许从Abstract1*to 转换Abstract2*?这是一个标准吗?

4

4 回答 4

6

你所描述的是所谓的交叉演员。对于dynamic_cast<T>(v),标准在 [expr.dynamic.cast]/8 中指定

如果CT指向或引用的类类型,则运行时检查在逻辑上执行如下:

  • 如果在指向(引用)的最派生对象中vv指向(引用)对象的公共基类子C对象 [..]

  • 否则,如果v指向(引用)到最派生对象的public基类子对象,并且最派生对象的类型具有类型为 的基类,即C明确且public,则结果指向(引用)到C大多数派生对象。

即使没有关于Foo' 在包含演员表的翻译单元中存在的信息,这也将起作用。

你也应该看看这个问题

于 2014-11-20T14:38:21.873 回答
4

是的,它会起作用。

dynamic_cast基于RTTI。RTTI 在这里提供的信息足以确定所指向对象的实际动态类型。根据定义,RTTI 是一个运行时概念,指向对象的动态类型也是如此(Foo 的定义在编写所述转换的编译单元中不可用的事实是一个编译时概念,这里没有相关性)。

  • 如果指向的对象实际上是 Foo,则 dynamic_cast 将在运行时成功。
  • 如果它不是指向从 Abstract2 派生的对象的指针,它将失败(返回空指针)。

细节

一个可能的实现dynamic_cast是在对象的内存布局的开头查找一个特殊成员(或者它可以存储在 v-table 中)。此结构可以包含一个值,该值标识对象的动态类型。在某个地方,编译器会生成一个静态表,复制有关程序继承图的所有信息。在运行时,强制转换将提取实例的类型标识符,并根据静态表检查它。如果此标识符引用派生自 Abstract2 的类型,则强制转换是有意义的(并且代码可以正确返回指向Abstract2对象接口的偏移量的指针)。

即使是这种幼稚的实现也不需要知道Foo编写强制转换的编译单元中的知识。

于 2014-11-20T14:36:09.000 回答
2

对于此代码:

void func(Abstract1* a1)
{
    Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
    ...
}

你在问:

如果a1是指向一个Foo对象,动态转换会返回一个有效的对象指针吗?

答案是肯定的:

  • 在运行时,动态转换会将 V-Table 标识a1class Foo.
  • 由于class Foo继承自class Abstract2,动态转换将返回一个有效指针。
于 2014-11-20T14:42:25.397 回答
1

好吧,你可以简单地尝试一下

#include <cassert>

struct IBase1
{
    virtual void foo() = 0;
    virtual ~IBase1() {}
};

struct IBase2
{
    virtual void bar() = 0;
    virtual ~IBase2() {}
};

struct Derived : IBase1, IBase2
{
    void foo() {}
    void bar() {}
};

int main()
{
    Derived d;

    IBase1* ptr = &d;
    assert(dynamic_cast<IBase2*>(ptr));
    assert(dynamic_cast<Derived*>(ptr));
}

// Compiles successfully

这是证据:

[C++11: 5.2.7/8]:如果CT指向或引用的类类型,则运行时检查在逻辑上执行如下:

  • 如果在 by 指向(引用)的最派生对象中vv指向(引用)对象的public基类子C对象,并且如果只有一个类型的对象从结果指向C(引用)的子对象派生出来,则指向(引用)v) 到那个C对象。
  • 否则,如果v指向(引用)到最派生对象的公共基类子对象,并且最派生对象的类型具有类型为 的C明确基类,public则结果指向(引用)的C子对象派生最多的对象。

  • 否则,运行时检查失败

通俗地说,我们称之为交叉铸造

语言没有规定在“当前”翻译单元中知道最派生对象的类型;实现这项工作取决于实现,并且在常见的“虚拟表”模型中,确实如此。

于 2014-11-20T14:39:04.830 回答