使用shared_ptr向量的类的析构函数会导致错误

时间:2016-11-16 10:30:29

标签: c++ oop vector smart-pointers

我有一个类Animal,它是少数不同动物的基类,以及一个类,它将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()它也会崩溃。

4 个答案:

答案 0 :(得分:2)

Dog d1, d2;
Cat c1, c2;

这些对象具有自动存储持续时间。他们并不意味着拥有智能指针。

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

herd += new Dog;

答案 1 :(得分:1)

您的问题是使用自动存储持续时间传递变量的地址。 请参阅: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>();

答案 2 :(得分:1)

  

它有什么问题?

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

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);
}

答案 3 :(得分:0)

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

正如其他人所提到的,第一个是shared_ptr假设它管理的对象是动态创建的(使用运算符new),因此使用运算符delete释放它(除非自定义删除器)在构造代码不执行的shared_ptr时提供。将operator delete应用于具有auto存储持续时间的对象会导致未定义的行为。

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