我有一个类Animal,它是几个不同动物的基类和一个类Herd,它将shared_prt存储到向量中的动物。我不熟悉智能指针,但是为了处理继承,我不得不在代码中使用它们。它似乎工作得很好,但在我的代码得到"Herd"的析构函数后,它抛出了一个错误。有什么问题吗?
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;
您的问题是传递具有自动存储持续时间的变量的地址。参见:c++中的堆栈、静态和堆
你的代码是这样写的:
创建一个具有自动存储持续时间的变量:
Dog d1
它将在超出作用域后自动销毁(在您的例子中是main函数的末尾)
然后,你把它的地址传递给一个函数,这个函数将这个地址存储在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>();
怎么了?
在这段代码中,你试图创建shared_ptr
与堆栈分配的对象。这将导致该对象的双重删除,第一次删除发生在堆栈对象超出作用域时。第二次发生在delete
算子的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
时提供了自定义删除器,而您的代码没有这样做)。对存储时间为auto
的对象应用delete操作符会导致未定义的行为。
第二个问题——在解决第一个问题之后你最终会遇到的——是Animal
类没有virtual
析构函数。即使使用操作符new
创建对象,操作符delete
也会导致从Animal
派生的实际类型对象的未定义行为(即,如果实际对象的类型为Cat
, Dog
等)。