C++11智能指针的向量

12

假设我们有以下代码。 我们有以下类:

  • Animal作为抽象类
  • Dog和Bird是Animal的子类
  • Zoo保存了所有动物

_

class Animal
{
public:
    Animal();
    void HasWings() = 0;
};

class Bird : public Animal
{
public:
    Bird() : Animal() {}
    void HasWings() override { return true; }
};

class Dog : public Animal
{
public:
    Dog() : Animal() {}
    void HasWings() override { return false; }
};

class Zoo
{
public:
    Zoo() {}
    void AddAnimal(Animal* animal) { _animals.push_back(animal); }
    ...
    std::vector<Animal*> _animals;
};

void myTest()
{
    Zoo myZoo;
    Bird* bird = new Bird();
    Dog* dog = new Dog();

    myZoo.AddAnimal(bird);
    myZoo.AddAnimal(dog);

    for (auto animal : myZoo._animals)
    {
        ...
    }
    ...
}

我希望用智能指针的向量代替指针的向量,即:

std::vector<std::shared_ptr<Animal>> _animals;

我们该如何修改Zoo和myTest的代码呢?我在更新代码时遇到了困难,尤其是Zoo类中的"AddAnimal"方法。

auto bird = std::make_shared<Bird>();
auto dog = std::make_shared<Dog>();
myZoo.AddAnimal(bird);
myZoo.AddAnimal(dog);

鸟和狗是不同的种类


2
AddAnimal(std::shared_ptr<Animal> animal) - shared_ptr<Bird>shared_ptr<Dog> 会转换为 shared_ptr<Animal> ,因为前者两个派生自后者。另外,Animal::HasWings 未声明为 virtual ,且由于您已经有了一个虚成员函数,因此您可能还应该使 Animal 的析构函数成为 virtual - Praetorian
1个回答

15

std::shared_ptr的行为非常类似于裸指针,对于*->操作符也是如此(事实上,解引用操作符被“转发”到由std::shared_ptr存储的内部裸指针)。特别是,您可以使用std::shared_ptr作为虚基类进行沿着类层次结构进行虚派遣。例如,下面的代码确切地执行了一个人期望的操作,即在运行时调用适当的函数:

#include <iostream>
#include <memory>
#include <vector>

struct Base
{
    virtual void f() { std::cout << "Base::f()" << std::endl;}
    virtual ~Base() = default; // to silence -Wall warnings
};

struct Derived: Base
{
    void f() override { std::cout << "Derived::f()" << std::endl;}
};

int main()
{
    std::vector<std::shared_ptr<Base>> vsp; // access Derived via shared_ptr to Base

    auto base = std::make_shared<Base>();
    auto derived = std::make_shared<Derived>();

    vsp.push_back(base);
    vsp.push_back(derived);

    for(auto&& elem: vsp)
        elem->f(); // virtual dispatch
}

因此,大多数情况下,将Animal*替换为std::shared_ptr<Animal>就足够了,代码将正常工作。使用std::unique_ptr会更加复杂,因为后者是一种移动类型(您无法复制它),所以必须更加小心。


1
你是对的。如果我使用std::unique_ptr而不是std::shared_ptr,因为我不能像以前那样创建一个返回Animal指针的方法。 即,“Animal* GetAnimal(int index);”。 - user2434918
假设Base和Derived是有一些函数和成员的复杂类,您将如何在main函数结束前销毁向量vsp并避免内存泄漏? - Annie
1
@Annie 首先执行 vsp.resize(0),然后你也可以执行 base.reset(); derived.reset(); 来释放最初的共享指针(记住,将其推入向量时引用计数会增加1)。 - vsoftco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接