C++之dynamic_cast

C++之dynamic_cast

前言

dynamic_cast运算符牵扯到的面向对象的多态性跟程序运行时的状态,所以不能完全的使用传统的转换方式来替代。因此是最常用,最不可缺少的一个运算符,与static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。 更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。从这个方面来看,似乎dynamic_cast又和reinterpret_cast是一致的,但实际上,它们还是存在着很大的差别。引用

 如果转换成功, dynamic_cast将返回一个new-type类型的值。如果转换失败,new-type是指针类型,则返回该类型的空指针。如果转换失败并且new-type是引用类型,它将抛出一个与std::bad_cast类型的处理程序匹配的异常

dynamic_cast

 dynamic_cast(expression)

Note:

 只有以下转换可以使用dynamic_cast完成,除非这些转换会丢弃constness或volatile。

  1. 如果expression的类型恰好是new-type或new-type的一个较少cv(constness and volatile)限定版本,则结果是expression的值,类型为new-type。(换句话说,dynamic_cast可以用来添加constness或volatile。隐式转换static_cast也可以执行此转换)
  D* d1 = new D();
  D* d2 = dynamic_cast<D*>(d1);
  d2->hello();
  const D *d3 = dynamic_cast<const D *>(d1);
  1. 如果expression的值是空指针值,则结果是new-type的空指针值。
   D *ptr = NULL;
   D* ptr2 = dynamic_cast<D*>(ptr);
   if(ptr2 == NULL){
    
    
      printf("ptr2 is NULL\n");
   }

控制台输出

2) ptr2 is NULL

  1. 如果new-type是一个指向Base的指针或引用,expression的类型是一个指向Derived的指针或引用,其中BaseDerived的一个唯一的、可访问的基类,则结果是一个指向由expression指向或定义的Derived对象中的基类子对象的指针或引用。(注意:隐式转换和static_cast也可以执行此转换)

  1. 如果expression是指向多态类型的指针,而new-type是指向void的指针,则结果是指向由expression指向或引用的派生最底层的对象的指针。


5. 如果expression是指向多态类型Base的指针或引用,而new-type是指向Derived的指针或引用,则执行运行时检查,这应该是dynamic_cast使用最多的场景:

  • a)如果在该对象中,expression是派生的公共基的指向/引用,并且如果只有一个派生类型的对象是从expression所指向/定义的子对象派生出来的,则强制转换的结果指向/引用该派生对象。(downcast”)
D d;      // the most derived object
A &a = d; // upcast, dynamic_cast may be used, but unnecessary
[[maybe_unused]] D &new_d = dynamic_cast<D &>(a); // downcast
new_d.hello();
  • b)否则,如果expression是派生最底层的对象的公共基类的指针或引用,同时,派生最底层的对象有一个类型为derived的明确的公共基类,则转换的结果指向/引用了该derived。(sidecast)

    D d;      // the most derived object
    A &a = d; // upcast, dynamic_cast may be used, but unnecessary   
    [[maybe_unused]] B &new_b = dynamic_cast<B &>(a); // sidecast
    new_b.hello();
    
  • c)否则,运行时检查失败。如果在指针上使用dynamic_cast,则返回new-type类型的空指针值。如果它用于引用,则抛出异常std::bad_cast。


6. 当dynamic_cast在构造函数或析构函数中使用时(直接或间接),expression指向当前正在构造/销毁的对象,则该对象被认为是派生最底层对象。如果new-type不是指向构造函数/析构函数自己的类或基类之一的指针或引用,则该行为未定义, 如下例中的6)

 类似于其他强制转换表达式,其结果为:

  • 如果new-type是一个左值引用类型(表达式必须是一个左值),则结果是一个左值,

  • 如果new-type是一个右值引用类型(表达式可以是左值,或者右值(直到c++ 17)必须是一个完整类类型的glvalue (从c++ 17开始)), 则结果是xvalue.

  • 如果new-type是一个指针类型,则是一个prvalue.

 几个value的定义,可以参考https://cloud.tencent.com/developer/article/1493839

示例:

struct V
{
    
    
   virtual void f() {
    
    } // must be polymorphic to use runtime-checked dynamic_cast
};

struct A : virtual V
{
    
    
   void hello()
   {
    
    
        printf("hello, I am A\n");
   }
};

struct B : virtual V
{
    
    
   B(V *v, A *a)
   {
    
    
       	
        // casts during construction (see the call in the constructor of D below)
        dynamic_cast<B *>(v); // well-defined: v of type V*, V base of B, results in B*
        dynamic_cast<B *>(a); // undefined behavior: a has type A*, A not a base of B
   }
   void hello()
   {
    
    
        printf("hello, I am B\n");
   }
};

struct D : A, B
{
    
    
   //6)
   D() : B(static_cast<A *>(this), this) {
    
    }
   void hello(){
    
    
    printf("hello, I am D\n");
   }
};

struct Base
{
    
    
   virtual ~Base() {
    
    }
};

struct Derived : Base
{
    
    
   virtual void name() {
    
    }
};
void test_dynamic_cast(){
    
    

   D d;      // the most derived object
   A &a = d; // upcast, dynamic_cast may be used, but unnecessary

   [[maybe_unused]] D &new_d = dynamic_cast<D &>(a); // downcast
    new_d.hello();
   [[maybe_unused]] B &new_b = dynamic_cast<B &>(a); // sidecast
    new_b.hello();
   Base *b1 = new Base;
   if (Derived *d = dynamic_cast<Derived *>(b1); d != nullptr)
   {
    
    
        std::cout << "downcast from b1 to d successful\n";
        d->name(); // safe to call
   }

   Base *b2 = new Derived;
   if (Derived *d = dynamic_cast<Derived *>(b2); d != nullptr)
   {
    
    
        std::cout << "downcast from b2 to d successful\n";
        d->name(); // safe to call
   }

   delete b1;
   delete b2;
}

控制台输出

sh-4.4$ ./build/linux/x86_64/release/Class-convert 
hello, I am D
hello, I am B
downcast from b2 to d successful

猜你喜欢

转载自blog.csdn.net/dddgggd/article/details/129394181