【问题标题】:What happens when you dynamic_cast between derived classes with the same base class?当您在具有相同基类的派生类之间进行动态转换时会发生什么?
【发布时间】:2018-05-30 11:12:51
【问题描述】:

我试图弄清楚当你 dynamic_cast 从一个派生类到另一个派生类时会发生什么。为什么下面的代码会引发分段错误?请注意,我并没有尝试将此代码用于任何事情。我只是想了解发生了什么。

同样值得注意的是,同样的代码使用static_cast。我似乎找不到任何文档来详细说明这里发生的事情。谁能解释一下?

struct base 
{ 
    virtual void printing(){cout<<"base printing"<<endl;};
};
struct derived_1 :public base 
{ 
    virtual void printing(){cout<<"derived_1 printing"<<endl;};
};
struct derived_2 :public base 
{ 
    virtual void printing(){cout<<"derived_2 printing"<<endl;};
};

int main()
{
    base * b = new derived_2();
    derived_1 *d_1 = dynamic_cast<derived_1*>(b);

    // calling printing raises segmentation fault
    d_1->printing(); 
}

【问题讨论】:

  • 注意:尽管合法,但您应该从子类中的重写方法中删除 virtual 说明符,并添加 override,如 void printing() override { ... }

标签: c++11 polymorphism dynamic-cast static-cast


【解决方案1】:

转换为derived_1 将失败,因为derived_2 base 对象,但不是 derived_1 对象。因此,您不能“侧向转换”到所需的指针类型。

并不是说每当dynamic_cast 失败时,它都会返回nullptr (except for reference types)。这最终会导致您的代码出现分段错误(通常,我建议您在使用动态转换的对象之前始终添加 if (d_1 != nullptr))。

更新:

顺便说一句,这实际上是dynamic_cast必要性的一个很好的例子。即使您可能想在示例中使用static_cast 并且它会编译,但您将处理未定义的行为。使用static_cast 编译时不会出现任何问题,但实际上您会使用损坏的类型。假设derived_1::printing() 访问了derived_1::a 中不存在的某个变量derived_2。通过将derived_2 对象(没有a)静态转换为derived_1 对象d_1,您会错误地假设d_1 包含一些有效的a,但事实并非如此.

例子:

// ...
struct derived_1 :public base
{
    const int a = 123;
    void printing() override {cout<<"derived_1 printing " << endl;}
    void foo() { cout << "Foo using constant = " << a << endl; }
};

// ...
int main()
{
    base * b = new derived_2();
    derived_1 *d_1 = static_cast<derived_1*>(b);
    d_1->printing(); // Will call derived_2::printing(), which is not what you expect!
    d_1->foo();      // Won't show a = 123 but some nonesense (mostly 0),
                     // because a is not defined in derived_2.
}

【讨论】:

  • 嗯,有道理!谢谢您的帮助。我很困惑,因为我实际测试的代码是调用 2 个函数。所以它读起来像 d_1->non_virtual_member_function_of_derived_1(); d_1->打印();第一个函数调用成功了,所以我只是假设 d_1 不是 nullptr。但是,我现在看到 nullptr 实际上可以调用成员函数。尽管这是未定义的行为,但在这种情况下它是成功的
  • 但是,我现在看到 nullptr 实际上可以调用成员函数。:不,你不能在 nullptr 上调用任何东西。
  • 这就是我的想法,但显然你可以。这是未定义的行为,但你可以做到,它不会总是引发分段错误。这在此 SO link 中进行了讨论
  • 您可以在空指针上调用非虚拟函数,尽管它被归类为未定义行为。然而,使用空指针调用虚函数将是致命的,因为需要知道对象的知识才能查找虚函数的实现——通常通过实例本身中的 VFT 指针。
猜你喜欢
  • 1970-01-01
  • 2011-08-03
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 2021-04-22
  • 2018-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多