在动态创建的智能指针向量元素上投射 static_cast

Casting static_cast on dynamically-created element of smartpointers vector

我正在学习 C++,但遇到了一个问题。我创建了一个智能指针向量,其中包含派生 class 的动态创建对象,但我无法在这些对象之一上投射 static_cast。我尝试了几种方法,但都没有用。

这是我的代码:

class Person {};
class Walker : public Person {};

std::vector<std::unique_ptr<Person>>v1;
for (int i = 0; i < 4; i++) { v1.push_back(std::unique_ptr<Person>(new Walker) ); }

//trying to cast static_cast on element on position 1:
std::unique_ptr<Person>ptr = move(v1[1]);
auto walkers = static_cast<Walker*>(ptr);//doesnt work - no suitable conversion function

这可能不是您想要的!

Person class 应该有一个 Walker 实现的虚拟接口,因此您不需要将 Person 指针转换为 Walker 指针。

也就是说:

解决您当前的问题:

std::unique_ptr<Walker> getWalkerFromPerson(std::unique_ptr<Person> person)
{
    // Note there is still an issue here.
    // If this is not a Walker then it will leak
    // the person as dynamic_cast will give you a nullptr
    return std::unique_ptr<Walker>(dynamic_cast<Walker*>(person.release()));
}

然后这样调用:

auto walkers = getWalkerFromPerson(std::move(v1[0]));

一些建议:

  1. 您需要释放 person(因此它不会删除它)。
  2. 您需要使用 dynamic_cast(从父类型转换为未知(运行-时间已知)子类型)。

您不能强制转换 std::unique_ptr,但您可以为其托管指针执行此操作:

auto* walkers = static_cast<Walker*>(ptr.get());

您需要先从 unique_ptr 中解包原始指针:

auto walkers = static_cast<Walker*>(ptr.get());

注意指针的所有权仍然属于ptr,也就是std::unique_ptr<Person>。您可以这样转让所有权:

std::unique_ptr<Walker> walkers(static_cast<Walker*>(ptr.release()));

如果你愿意,你可以这样写一个函数

template<typename Target, typename Source>
auto static_cast_unique(std::unique_ptr<Source> ptr) {
    return std::unique_ptr<Target>(static_cast<Target*>(ptr.release()));
}

所以你可以这样写

auto walkers = static_cast_unique<Walker>(std::move(v1[1]));

现在,您的代码存在一些问题:

  • Person 需要一个虚拟析构函数。您有 Walker 个对象,属于 std::unique_ptr<Person> 个。这本身没问题,但是当那些 unique_ptr 试图销毁他们的对象时,他们会调用 ~Person,而不是 ~Walker(因为这是他们的类型要求做的),这将是UB。给 Person 一个 virtual ~Person()(并且 Walker 将继承 virtualness)

    class Person { public: virtual ~Person() = default; };
    class Walker : public Person {};
    
  • std::unique_ptr(new ...)最好写成std::make_unique

    for (int i = 0; i < 4; i++) v1.push_back(std::make_unique<Walker>());
    

    std::unique_ptrs 会自动向上转换。

  • 在这种情况下,使用 static_cast 似乎没问题,因为您 知道 您实际指向的 Person*一个Walker。但是,在一般情况下您会希望使用 dynamic_cast,这样您就可以检测到对象 何时不是您期望的类型 。我会使用这样的函数

    template<typename Target, typename Source>
    std::unique_ptr<Target> dynamic_cast_unique(std::unique_ptr<Source> &&ptr) {
        if(auto ret = dynamic_cast<Target*>(ptr.get())) {
            ptr.release();
            return std::unique_ptr<Target>(ret);
        } else return nullptr;
    }
    

    哪个用的像

    auto walkers = dynamic_cast_unique<Walker>(std::move(v1[1]));
    

    如果成功,walkers 将拥有之前由 v1[1] 拥有的对象,而 v1[1] 将为空。否则,walkers为空,v1[1]不变。