使用“shared_ptr”行为从基类转换为派生类

问题描述

我无法理解 shared_ptr 的转换。 This thread 很好地解释了普通指针的行为,结果非常直观 - 正是我所期望的。但是,shared_ptr 显示了不同的结果 - 我创建了 3 个类,一个 Base 和两个 Derived 并使用 ***_ptr_cast 进行了一些尝试:

#include <iostream>
#include <memory>

using namespace std;

struct Base 
{
    virtual void f() { cout << "Base" << endl; }
    
    string name = "Base";
};

struct FirstDerived : public Base
{
    void f() override { cout << "FirstDerived" << endl; }
    
    void firstDerived() { cout << "FirstDerived only method" << endl; }
    
    string name = "FirstDerived";
};

struct SecondDerived : public Base
{
    void f() override { cout << "SecondDerived" << endl; }
    
    void secondDerived() { cout << "SecondDerived only method" << endl; }
    
    string name = "SecondDerived";
};


int main()
{
    FirstDerived fd;
    std::shared_ptr<Base> bf = make_shared<Base>(fd);
    std::shared_ptr<FirstDerived> fdp = std::static_pointer_cast<FirstDerived>(bf);
    
    if (fdp)
    {
        fdp.get()->f();
        fdp.get()->firstDerived();
        //cout << fdp.get()->name;
    }
    
    FirstDerived sd;
    std::shared_ptr<Base> bs = make_shared<Base>(sd);
    std::shared_ptr<SecondDerived> sdp = std::static_pointer_cast<SecondDerived>(bs);
    
    if (sdp)
    {
        sdp.get()->f();
        sdp.get()->secondDerived();
        //cout << sdp.get()->name;
    }
   
   return 0;
}

这个程序的输出显示(有趣的是 cout << fdp.get()->name; 是不可能的,因为它会出现段错误):

Base
FirstDerived only method
Base
SecondDerived only method

进一步研究后我得出结论,static_cast 可能是错误的演员表,所以我将它们与 dynamic_cast 交换。然而,动态版本永远不会返回有效值,即使我正在转换为正确的派生版本。 希望的结果是,只有当初始对象的类型与下一步中要转换的对象的类型相同时,才会有结果。否则它应该为空。

我应该如何正确地执行此操作,是否有我正在搜索内容的强制转换版本?

解决方法

您需要将 std::make_shared 中的类型分别更改为 FirstDerivedSecondDerived。 此外,sd 的类型应为 SecondDerived

int main()
{
    FirstDerived fd;
    std::shared_ptr<Base> bf = make_shared<FirstDerived>(fd);
    std::shared_ptr<FirstDerived> fdp = std::static_pointer_cast<FirstDerived>(bf);
    
    if (fdp)
    {
        fdp.get()->f();
        fdp.get()->firstDerived();
        cout << fdp.get()->name;
    }
    
    SecondDerived sd;
    std::shared_ptr<Base> bs = make_shared<SecondDerived>(sd);
    std::shared_ptr<SecondDerived> sdp = std::static_pointer_cast<SecondDerived>(bs);
    
    if (sdp)
    {
        sdp.get()->f();
        sdp.get()->secondDerived();
        cout << sdp.get()->name;
    }
   
   return 0;
}

这是一个 live demo

编辑 根据 OP 的要求(example application 没有智能指针),他想向向量添加智能指针并动态调度存储的类型。为此,可以简单地创建一个向量 std::vector<std::shared_ptr<Base>> vec 并通过 vec.push_back(std::make_shared<FirstDerived>(fd))vec.push_back(std::make_shared<SecondDerived>(sd)) 添加元素。这是一个 example

int main()
{
    FirstDerived fd;
    SecondDerived sd;

    vector<std::shared_ptr<Base>> vec;
    
    vec.push_back(std::make_shared<FirstDerived>(fd));
    vec.push_back(std::make_shared<SecondDerived>(sd));
    
    auto fdp = std::dynamic_pointer_cast<FirstDerived>(vec[0]);
    auto sdp = std::dynamic_pointer_cast<FirstDerived>(vec[1]);
    
    // true
    if (fdp)
    {
        fdp->f();
        fdp->firstDerived();
        cout << fdp->name;
    }
    
    // false
    if (sdp)
    {
        sdp->f();
        cout << sdp->name;
    }
   
   return 0;
}

这给出了所需的输出:

FirstDerived
FirstDerived only method
FirstDerived