向量为 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
, 等等).
我有一个 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
, 等等).