dynamic_cast的介绍与应用

发布时间:2023-05-20

一、基础知识

dynamic_cast是C++中的一种类型转换操作符,用于将基类指针或引用转换为派生类指针或引用,或者将指向虚基类的指针或引用转换为指向派生类的指针或引用。 使用dynamic_cast进行转换时,它会在运行时进行类型检查,如果转换非法,则返回一个空指针或抛出一个std::bad_cast异常。

二、dynamic_cast的语法

dynamic_cast有两种语法形式,一种是将指针或引用转换为派生类指针或引用,另一种是将指针或引用转换为虚基类指针或引用。

// 第一种语法形式,将指针或引用转换为派生类指针或引用
typename dynamic_cast<派生类类型*>(基类指针或引用);
// 第二种语法形式,将指针或引用转换为虚基类指针或引用
typename dynamic_cast<虚基类类型*>(指向虚基类的指针或引用);

三、dynamic_cast的应用

1、动态类型识别

dynamic_cast最基本的用途就是在运行时判断一个对象的实际类型。例如,定义了一个基类Animal和它的两个派生类CatDog,现在有一个指向Animal对象的基类指针,我们需要在运行时判断它指向的是Cat对象还是Dog对象。

class Animal {
public:
    virtual ~Animal() {}
};
class Cat : public Animal {
public:
    void meow() {}
};
class Dog : public Animal {
public:
    void bark() {}
};
int main()
{
    Animal* animal_ptr = new Cat();
    Cat* cat_ptr = dynamic_cast<Cat*>(animal_ptr);
    if (cat_ptr) {
        std::cout << "This is a cat." << std::endl;
        cat_ptr->meow();
    }
    Dog* dog_ptr = dynamic_cast<Dog*>(animal_ptr);
    if (dog_ptr) {
        std::cout << "This is a dog." << std::endl;
        dog_ptr->bark();
    }
    delete animal_ptr;
    return 0;
}

运行结果:

This is a cat.

上面的示例中,我们先定义了一个Animal类作为基类,然后分别定义了CatDog派生类,接着我们创建一个基类指针animal_ptr,并将其指向一个Cat对象。 通过使用dynamic_castanimal_ptr指针转换为Cat指针cat_ptr,并对其进行判空操作,我们可以在运行时判断animal_ptr指向的是一个Cat对象。同样地,我们也可以将animal_ptr指针转换为Dog指针dog_ptr,并对其进行判空操作,判断animal_ptr指向的是一个Dog对象。

2、避免类型转换错误

另一个使用dynamic_cast的原因是防止类型转换错误。当我们需要将一个基类指针或引用转换为派生类指针或引用时,如果使用static_cast进行转换,有可能会出现指针或引用指向的并不是派生类对象的情况,从而导致程序出现未定义的行为。 使用dynamic_cast时,如果转换非法,则返回一个nullptr或抛出一个std::bad_cast异常,从而避免了这种情况的发生。

class Base {
public:
    virtual void show() {}
};
class Derived : public Base {
public:
    void something() {}
};
int main()
{
    Base* base_ptr = nullptr;
    Derived* derived_ptr = static_cast<Derived*>(base_ptr); // 编译通过
    derived_ptr->something(); // 运行时错误
    Base* another_base_ptr = new Derived();
    Derived* another_derived_ptr = dynamic_cast<Derived*>(another_base_ptr);
    another_derived_ptr->something(); // 正常运行
    delete another_base_ptr;
    return 0;
}

上面的示例中,我们先定义了一个Base类作为基类,然后定义了一个Derived派生类。在main函数中,我们先将指针base_ptr初始化为nullptr,然后使用static_cast将其转换为Derived指针derived_ptr,并调用somthing函数。由于base_ptr指向的并不是Derived对象,运行时会出现未定义的行为。 相比之下,在另一个示例中,我们定义了一个Base指针another_base_ptr,并将其指向一个Derived对象。接着,我们使用dynamic_castanother_base_ptr转换为Derived指针another_derived_ptr,并调用其somthing函数。由于转换是合法的,程序可以正常运行。

3、基类向下转换

在一些情况下,我们需要在基类中获得派生类对象实例的指针或引用。这时,我们可以使用dynamic_cast将基类指针或引用转换为派生类指针或引用,然后使用这个指针或引用。

class Shape {
public:
    virtual ~Shape() {}
};
class Circle : public Shape {
public:
    void draw() {}
};
class Square : public Shape {
public:
    void draw() {}
};
int main()
{
    std::vector<Shape*> shapes = {new Circle(), new Square()};
    for (auto shape : shapes) {
        if (auto* circle = dynamic_cast<Circle*>(shape)) {
            circle->draw();
        }
        else if (auto* square = dynamic_cast<Square*>(shape)) {
            square->draw();
        }
    }
    for (auto shape : shapes) {
        delete shape;
    }
    return 0;
}

上面的示例中,我们定义了一个Shape基类和两个派生类CircleSquare。然后,我们将多个Shape对象添加到一个大小为2的vector中,在循环中遍历这个vector,并使用dynamic_cast将每个对象转换为CircleSquare指针,并调用相应的draw函数。

4、虚基类转换

在具有虚基类的多重继承中,从一个派生类转换到另一个派生类时,我们可能需要使用dynamic_cast,以将基派生类的指针转换为派生类的指针。

class Base {
public:
    virtual ~Base() {}
};
class Intermediate1 : virtual public Base {};
class Intermediate2 : virtual public Base {};
class Derived : public Intermediate1, public Intermediate2 {};
int main()
{
    Base* base_ptr = new Derived();
    Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
    if (derived_ptr) {
        std::cout << "Cast successful." << std::endl;
    }
    else {
        std::cout << "Cast failed." << std::endl;
    }
    delete base_ptr;
    return 0;
}

上面的示例中,我们定义了一个Base类和两个Intermediate虚基类,同时定义了一个Derived派生类,通过使用虚基类继承方式,使得Derived类同时继承自Intermediate1Intermediate2,从而形成一个虚基类继承结构。 接着,在main函数中,我们将一个Derived类对象的指针转换为Base指针,并使用dynamic_cast将其转换为Derived指针derived_ptr。由于这种转换是合法的,程序可以输出“Cast successful”。

四、总结

本文介绍了C++中的dynamic_cast类型转换操作符,并通过多个方面的示例对其进行了详细的阐述和应用。具体而言,我们研究了dynamic_cast的基础知识、语法形式和应用场景,分别举了动态类型识别、避免类型转换错误、基类向派生类转换和虚基类转换这几个方面的应用。