[c++]为什么我的类析构函数被调用两次?

[c++]Why is my class destructor called twice?

本文关键字:两次 调用 c++ 为什么 我的 析构函数      更新时间:2023-10-16

我有这样的代码,

#include <iostream>
#include <string>
using namespace std;
class Heart {
private:
int bpm;
public:
Heart(int bpm) : bpm(bpm) {}
int getBPM() {
return bpm;
}
};
class Kidney {
private:
double PercentFunction;
public:
Kidney() : PercentFunction(0) {}
Kidney(double pf) : PercentFunction(pf) {}
double getPF() {
return PercentFunction;
}
};
class Person {
private:
string fname, lname;
int age;
Heart h;
Kidney* k; 
public:
Person(string fn, string ln, int age, int bpm, double kpf1, double kpf2) : fname(fn), lname(ln), age(age), h(bpm) {
k = new Kidney[2];
k[0] = Kidney(kpf1);
k[1] = Kidney(kpf2);
cout << fname << " " << lname << ", aged " << age << ". Heart BPM : " << bpm <<
". Kidneys' percent function indices: " << k[0].getPF() << " and " << k[1].getPF() << '.' << endl;
}
~Person() {
cout << "A person is dying!" << endl;
delete[] k;
}

};

int main() {
Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
}

然后我运行我的代码,弹出一个错误(调试断言失败!您还可以看到析构函数被调用了两次。但是如果我删除~Person中的delete [] k;,就不会有这样的弹出错误。

Person 构造函数中有动态分配:

k = new Kidney[2];
k[0] = Kidney(kpf1);
k[1] = Kidney(kpf2);

所以我认为我应该在析构函数中删除 k。 我的问题是为什么析构函数被调用两次以及如何解决错误?

我正在使用VS 2013。

谢谢!

问题如下。在行中

Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98);

您正在复制初始化p,即您正在创建一个临时的,然后将其复制到Person p;。最后,临时Person("Jack", "Bowen", 24, 60, 0.99, 0.98);被销毁,因此您的Kidney*指针悬空,因为您没有实现复制构造函数并且副本很浅(即指针本身正在被复制,而不是它指向的对象)。并且您的析构函数被调用两次,因为它首先是在临时结束时(在语句结束时)调用的,然后在main()结束时Person p超出范围时再次调用。

每当类有指针时,实现其复制构造函数和赋值运算符。或者更好的是,使用智能指针,如std::shared_ptr,甚至更好的标准容器,跟踪其动态内存,如std::vector/std::list等。

快速而肮脏地修复您的代码(但实际上,您必须实现复制构造函数,因为您将遇到所有其他类型的问题,例如,从函数返回Persons 或按值传递Persons 时):

Person p("Jack", "Bowen", 24, 60, 0.99, 0.98);

这避免了任何临时操作,并使用直接初始化。

PS:在g++,编译-Weffc++警告你这些问题,

警告:"class Person"具有指针数据成员 [-Weffc++],但不会覆盖"Person(const Person&)" [-Weffc++]

或"operator=(const Person&)" [-Weffc++]

不过,我不确定VS是否存在这样的编译器标志。

问题出在您的生产线上

Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98);

这将构造两个对象:一个在=的右侧,一个在左侧。由于您没有定义复制构造函数,因此左侧的构造函数将简单地复制与右侧构造函数完全相同的指针。您提到的两个析构函数是这两个对象的,而=左侧的析构函数是导致问题显现的析构函数。

要解决此问题,您可以执行以下操作之一:

  1. 正确定义一个复制构造函数,该构造函数不会复制指针,而是分配新指针、复制内部对象等。

  2. 更好的方法是将指针替换为现成的类,该类为您执行这些操作,例如vector

如前所述,添加一个复制构造函数/赋值操作,就可以了。 但是,如果您只想解决此问题,则使用指针将很容易。

int main() {
Person *p = new Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
}