2. 动态转换

动态转换dynamic_cast)是用于含有虚函数的类创建的对象之间的类型转换,主要是用于按继承链安全向下转型转换或跨层级转换。

语法

dynamic_cast<新类型>(表达式)

动态转换 的过程是根据虚表进行判断。

dynamic_cast 是在运行时进行的类型转换,动态转换失败的情况有:

  1. 在指针转换时,转换失败返回 nullptr
  2. 在引用转换时转换失败会抛出 std::bad_cast 类型的异常。

示例

// filename: dynamic_cast.cpp
#include <iostream>
#include <exception>

using namespace std;

class Point {
    public:
        Point(float ax=0, float ay=0):x(ax), y(ay) { }
        // 注意以下函数已经改成虚函数
        virtual void info(void) {
            cout << "点(" << x << "," << y << ")\n";
        }
    public:
        float x;
        float y;
};
class Circle : public Point {
    public:
        Circle(float ax, float ay, float radius)
            : Point(ax, ay), r(radius) { }
        void info(void) {  // 子类的此函数也为虚函数
            cout << "圆(" << x << "," << y << "," << r << ")\n";
        }
    public:
        float r; // 半径
};

int main(int argc, char * argv[]) {
    Point p1(1, 2);
    Circle c1(3, 4, 5);
    Point * pp;
    Circle *pc; 

    pp = dynamic_cast<Point*>(&c1);
    if (nullptr == pp) 
        printf("dynamic_cast<Point*>(&c1): 转换失败!\n");
    else 
        pp->info();

    pc = dynamic_cast<Circle*>(&p1);
    if (nullptr == pc) 
        printf("dynamic_cast<Circle*>(&p1): 转换失败!\n");
    else 
        pc->info();

    try {
        Point & rp = dynamic_cast<Point&>(c1);
        rp.info();
    } catch (std::bad_cast &err) {
        printf("dynamic_cast<Point&>(c1): 转换失败!\n");
    }
    try {
        Circle & rc = dynamic_cast<Circle&>(p1);
        rc.info(); // 运行结果有错。
    } catch (std::bad_cast &err) {
        printf("dynamic_cast<Circle&>(p1): 转换失败!\n");
    }

    return 0;
}

编译和运行结果如下:

weimz@mzstudio:~/old$ g++ -o dynamic_cast dynamic_cast.cpp
dynamic_cast.cpp: In function ‘int main(int, char**)’:
dynamic_cast.cpp:41:10: warning: ‘dynamic_cast<class Circle*>(Point p1) can never succeed
   41 |     pc = dynamic_cast<Circle*>(&p1);
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~
weimz@mzstudio:~/old$ ./dynamic_cast (3,4,5)
dynamic_cast<Circle*>(&p1): 转换失败!
圆(3,4,5)
dynamic_cast<Circle&>(p1): 转换失败!

可见基于有虚函数的类创建的对象进行动态转换时,基类对象转为子类对象的指针或引用时都可能失败,返回空指针(指针)或触发错误(引用)。