• Index

基类和派生类相互转换

Reads: 4

基类和派生类的裸指针转换

基础示例 1

#include <iostream> // std::cout std::endl

class base
{
public:
    virtual ~base(void) = default;
};

class derived : public base
{
};

int main(void)
{
    // 派生类对象
    derived derivedobj;
    std::cout << "派生类对象的地址是:" << &derivedobj << std::endl;

    // 基类裸指针保存派生类对象的地址(隐式转换)
    base *pointer1 = &derivedobj;
    std::cout << "隐式转换后,基类裸指针保存的地址是:" << pointer1 << std::endl;

    // 基类裸指针保存派生类对象的地址(static_cast转换)
    base *pointer2 = static_cast<base *>(&derivedobj);
    std::cout << "static_cast转换后,基类裸指针保存的地址是:" << pointer2 << std::endl;

    // 用派生类裸指针保存基类裸指针的值(不能隐式转换)
    // derived *pointer3 = pointer1; // 去掉开头注释将会编译报错

    // 用派生类裸指针保存基类裸指针的值(dynamic_cast转换)
    derived *pointer4 = dynamic_cast<derived *>(pointer1);
    std::cout << "dynamic_cast转换后,派生类裸指针保存的地址是:" << pointer4 << std::endl;

    // 用派生类裸指针保存基类裸指针的值(static_cast转换)
    derived *pointer5 = static_cast<derived *>(pointer1);
    std::cout << "static_cast转换后,派生类裸指针保存的地址是:" << pointer5 << std::endl;

    return 0;
}

基础讲解 1

从输出结果可以看出,都是同一个内存地址。

派生类的地址或者指针赋值给基类指针时,可以隐式转换,也可以使用static_cast显示转换,不过一般都用隐式转换。

倒过来,基类指针赋值给派生类指针的时候,必须要显示转换。这个转换,使用dynamic_cast或者static_cast都不会报错。

基础示例 2

dynamic_caststatic_cast的区别。

#include <iostream> // std::boolalpha std::cout std::endl

class base
{
public:
    virtual ~base(void) = default;
};

class derived : public base
{
};

class test : public base
{
};

int main(void)
{
    std::cout << std::boolalpha;

    // 两个不同的派生类对象
    derived derivedobj;
    test testobj;

    // 基类裸指针保存派生类对象的地址
    base *pointer = &derivedobj;

    derived *pointer1 = dynamic_cast<derived *>(pointer);
    std::cout << "derived指针保存derivedobj的地址(dynamic_cast)" << std::endl;
    std::cout << "是否成功:" << (pointer1 != nullptr) << std::endl << std::endl;

    test *pointer2 = dynamic_cast<test *>(pointer);
    std::cout << "test指针保存derivedobj的地址(dynamic_cast):" << std::endl;
    std::cout << "是否成功:" << (pointer2 != nullptr) << std::endl << std::endl;

    derived *pointer3 = static_cast<derived *>(pointer);
    std::cout << "derived指针保存derivedobj的地址(static_cast):" << std::endl;
    std::cout << "是否成功:" << (pointer3 != nullptr) << std::endl << std::endl;

    test *pointer4 = static_cast<test *>(pointer);
    std::cout << "test指针保存derivedobj的地址(static_cast):" << std::endl;
    std::cout << "是否成功:" << (pointer4 != nullptr) << std::endl << std::endl;

    return 0;
}

输出结果:

derived指针保存derivedobj的地址(dynamic_cast)
是否成功:true

test指针保存derivedobj的地址(dynamic_cast):
是否成功:false

derived指针保存derivedobj的地址(static_cast):
是否成功:true

test指针保存derivedobj的地址(static_cast):
是否成功:true

基础讲解 2

derived类和test类并不是一个同一个类,它们不应该可以互相转换。

上面代码中,static_castderivedobj的地址转换成了test *类型,这样明显是不正确的。因为static_cast是在编译期进行转换,而分配堆内存是在程序运行的时候,内存地址每次也都不一样,编译器是无法安全检测的。

dynamic_cast可以在运行的时候,对地址的实际类型进行检测,当不能转换的时候,将会返回一个nullptr值。上面代码中,使用dynamic_cast尝试将derivedobj的地址转换成了test *类型,由于不能转换,所以转换的结果就是nullptr

所以,基类指针(或引用)转换成派生类指针(或引用)应该使用dynamic_cast,这样会更安全。

基础拓展

由于dynamic_cast是在运行的时候进行检测和转换的,所以会影响到运行时的性能。

不过,在良好的程序设计中,不会也不应该频繁地将基类指针(或引用)转换成派生类指针(或引用),也就是说,不应该频繁地使用dynamic_cast。如果你的代码频繁地使用了dynamic_cast,应该要考虑,继承是否设计好。

因此,在良好的程序设计中,合理使用dynamic_cast是没有问题的。

使用 dynamic_cast 转换引用

基础示例

#include <iostream> // std::cout std::endl
#include <typeinfo> // std::bad_cast

class base
{
public:
    virtual ~base(void) = default;
};

class derived : public base
{
};

class test : public base
{
};

int main(void)
try
{
    std::cout << std::boolalpha;

    // 两个不同的派生类对象
    derived derivedobj;
    test testobj;

    // 基类引用保存派生类对象
    base &object = derivedobj;

    derived &ref1 = dynamic_cast<derived &>(object);
    std::cout << "成功转换成derived引用" << std::endl;

    test &ref2 = dynamic_cast<test &>(object);
    std::cout << "成功转换成test引用" << std::endl;

    return 0;
}
catch (const std::bad_cast &)
{
    std::cout << "转换失败" << std::endl;
}

输出结果:

成功转换成derived引用
转换失败

基础讲解

dynamic_cast转换指针时,转换失败可以通过nullptr来表达,而引用就不能。所以dynamic_cast转换引用使用的是抛出异常std::bad_caststd::bad_cast在标准库typeinfo中。

基类和派生类的智能指针转换

基类和派生类的智能指针转换要使用std::dynamic_pointer_caststd::static_pointer_cast。由于std::dynamic_pointer_castdynamic_cast原理一样,std::static_pointer_caststatic_cast原理一样,所以下面就直接讲解这两个使用。

基础示例

#include <iostream> // std::cout std::endl
#include <memory> // std::shared_ptr std::dynamic_pointer_cast std::static_pointer_cast

class base
{
public:
    virtual ~base(void) = default;
};

class derived : public base
{
};

class test : public base
{
};

int main(void)
{
    std::cout << std::boolalpha;

    // 两个不同的派生类对象
    auto derivedobj = std::make_shared<derived>();
    auto testobj = std::make_shared<test>();

    // 隐式转换 derived->base
    std::shared_ptr<base> pointer1 = derivedobj;

    // static_pointer_cast derived->base
    auto pointer2 = std::static_pointer_cast<base>(derivedobj);

    // dynamic_pointer_cast base->derived
    auto pointer3 = std::dynamic_pointer_cast<derived>(pointer1);
    std::cout << (pointer3 == nullptr) << std::endl;

    // dynamic_pointer_cast base->derived
    auto pointer4 = std::dynamic_pointer_cast<test>(pointer1);
    std::cout << (pointer4 == nullptr) << std::endl;

    return 0;
}

输出结果:

false
true

Comments

Make a comment

  • Index

WARNING: You are using an old browser that does not support HTML5. Please choose a modern browser (Chrome / Microsoft Edge / Firefox / Sarafi) to get a good experience.