向量为 shared_ptr 的 class 的析构函数导致错误

Destructor of class with vector of shared_ptr causes error

我有一个 class 动物,它是几种不同动物的基础 class 和一个 class 兽群,它在向量中存储 shared_prt 动物。我不熟悉智能指针,但我不得不在我的代码中使用它们来处理继承。它似乎工作正常,但在我的代码到达 "Herd" 的析构函数后,它抛出一个 error。 怎么了?

class Animal {
public:
    Animal(string _sound) :
        sound(_sound) {}
    void give_sound() {
        cout << sound << " ";
    }
    bool operator==(Animal arg) {
        return (typeid(*this).name() == typeid(arg).name());
    }
protected:
    string sound;
};

class Dog : public Animal {
public:
    Dog() : Animal("woof") {}
};

class Cat : public Animal {
public:
    Cat() : Animal("meow") {}
};

class Cow : public Animal {
public:
    Cow() : Animal("moo") {}
};

class Herd {
public:
    Herd() {}
    ~Herd() {
        vec.clear();
    }

    Herd operator+(Animal *arg) {
        shared_ptr<Animal> ptr(arg);
        vec.push_back(ptr);
        return *this;
    }

    void operator+=(Animal *arg) {
        shared_ptr<Animal> ptr(arg);
        vec.push_back(ptr);
    }


    void make_noise() {
        vector<shared_ptr<Animal>>::iterator v = vec.begin();
        while (v != vec.end()) {
            (*v)->give_sound();
            v++;
        }
        cout << endl;
    }

private:
    vector<shared_ptr<Animal>> vec;
};

int main() {
    Herd herd;
    Dog d1, d2;
    Cat c1, c2;
    cout << "sound 1: " << endl;
    herd.make_noise();
    herd += &d1;
    herd += &c1;
    cout << "sound 2: " << endl;
    herd.make_noise();
    herd += &d2;
    herd += &c2;
    cout << "sound 3: " << endl;
    herd.make_noise();
    //herd = herd - &d1;
    //herd = herd - &d2;
    cout << "sound 4: " << endl;
    herd.make_noise();
    return 0;
}

编辑:没有 vec.clear() 它也会崩溃。

Dog d1, d2;
Cat c1, c2;

这些对象具有自动存储期限。他们并不意味着拥有智能指针来管理。

智能指针的用例是堆分配,例如:

herd += new Dog;

您的问题是传递具有自动存储持续时间的变量地址。 参见:Stack, Static, and Heap in C++

这就是您的代码中发生的情况:

您创建了一个具有自动存储持续时间的变量:

Dog d1

它会在超出作用域后自动销毁(在你的例子中是主函数结束)

然后,您将它的地址传递给将此地址存储在 SharedPtr 中的函数:

Herd operator+(Animal *arg) {
    shared_ptr<Animal> ptr(arg);
    vec.push_back(ptr);
    return *this;
}

这样做你告诉 shared_ptr 它负责这个对象的删除。 (简单来说,共享指针的析构函数将调用 delete Animal

因此您的对象将被释放两次,这是被禁止的。

您应该使用:

而不是使用原始指针
operator+(shared_ptr<Animal> arg)

并按以下方式分配您的对象:

std::shared_ptr<Dog> d1 = std::make_shared<Dog>();

What's wrong with it?

在此代码中,您试图创建 shared_ptr 具有堆栈分配的对象。这导致该对象的双重删除,第一个发生在堆栈对象超出范围时。第二个发生在 shared_ptr 运算符中的 shared_ptr 析构函数中。第二个无效,程序崩溃

Herd operator+(Animal *arg) {
    shared_ptr<Animal> ptr(arg);
    vec.push_back(ptr);
    return *this;
}

void operator+=(Animal *arg) {
    shared_ptr<Animal> ptr(arg);
    vec.push_back(ptr);
}

我可以看到两个明显的问题。

首先,正如其他人提到的那样,shared_ptr 假定它管理的对象是动态创建的(使用运算符 new),因此使用运算符 delete 释放它(除非在构造 shared_ptr 时提供了自定义删除器,而您的代码没有这样做。将运算符 delete 应用于具有 auto 存储持续时间的对象会导致未定义的行为。

第二个问题 - 在解决第一个问题后你最终会遇到 - 是 class Animal 没有 virtual 析构函数。即使对象是使用运算符 new 创建的,运算符 delete 也会导致派生自 Animal 的实际类型的对象发生未定义行为(即,如果实际对象的类型为 Cat , Dog, 等等).