从基础到衍生的下垂

时间:2019-05-22 21:07:39

标签: c++ dynamic-cast

我正在学习动态类型转换并搜索许多网站,得出的结论是,动态类型转换可以从基础进行转换,也可以从基础进行转换。 但是,当我从Base进行转换时,它给出了分段错误。 这是代码:

我期望从“ A”开始,但是即使在转换后也可以从D中获得


class A
{
  public:
  virtual ~A(){} 
  virtual void print()
  {
    std::cout << "from A"<<std::endl;
  }
  };
class B:  virtual public A
{
  public:
    void print()
  {
    std::cout << "from B"<<std::endl;
  }
};       
class C: virtual public A
{
  public:
    void print()
  {
    std::cout << "from C"<<std::endl;
  }
};      
class D: public B, public C
{
  public:
    void print()
  {
    std::cout << "from D"<<std::endl;
  }
};


int main(){
    A* b = new B;
    A* c = new C;
    A* d = new D;
    A* a = new A;

    B* down = dynamic_cast<B*>(a);  // this gives me problem
    down->print();

    A* up= dynamic_cast<A*>(d); // upcasten dont print from A either
    up->print();

2 个答案:

答案 0 :(得分:2)

使用dynamic_cast转换指针时,需要在访问前检查结果指针以查看强制转换是否有效。访问无效的指针会导致未定义的行为。

    B* down = dynamic_cast<B*>(a);
    if (down == 0) {
        std::cerr << "a is not a B!\n";
    } else {
        down->print();
    }

您不需要使用dynamic_cast进行“向上投射”。您可以将派生对象分配给父对象。如果该关系不存在,则会出现编译时错误。

    A* up= d;
    up->print();

由于print是虚拟方法,因此up->print()解析为派生的实现。但是,如果您希望查看基础的实现,则可以显式调用它。

    d->A::print();

Try it online!

答案 1 :(得分:1)

从Base到Derived以及从Derived到Base进行转换的能力并不是dynamic_cast的独特之处。关键特征是dynamic_cast在检查最派生的对象实际上匹配(或实际上包含)后,在运行时 执行此类转换。 )所需的目标类型。这意味着,如果转换失败,则错误也是运行时错误,因此您的程序应该在运行时处理这些错误。

那是什么错误? dynamic_cast可能会报告错误,如果在指针类型之间进行转换(例如,将X*转换为Y*,则返回空指针;如果正在转换,则抛出std::bad_cast异常)在引用类型之间进行比较,例如X&Y&

从具有静态类型a的{​​{1}}到A*的铸造可能会产生指向B*的有效指针或为null。后者正是您的示例所发生的情况:B实际上指向类型为a的完整对象,并且其中没有类型为A的子对象。因此,强制类型转换返回空指针,然后将其立即用于调用成员函数B。取消引用空指针值的任何尝试都是UB(未定义的行为),它很可能表现为段错误。

从这样的段错误中保护程序很容易:只需检查返回的值是否不为空:

down->print()

对于“从D”打印,是因为B* down = dynamic_cast<B*>(a); if (down) { // Use down all you want } else { // Report an error, skip some actions // or return from the function. // But don't use `down`! } 方法被声明为虚拟方法。这意味着即使您通过指向基础子对象的指针来调用最派生类的实现,也可以调用它。