[c++]为什么我的类析构函数被调用两次?
[c++]Why is my class destructor called twice?
我有这样的代码,
#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
等。
快速而肮脏地修复您的代码(但实际上,您必须实现复制构造函数,因为您将遇到所有其他类型的问题,例如,从函数返回Person
s 或按值传递Person
s 时):
Person p("Jack", "Bowen", 24, 60, 0.99, 0.98);
这避免了任何临时操作,并使用直接初始化。
PS:在g++
,编译-Weffc++
警告你这些问题,
或"operator=(const Person&)" [-Weffc++]
不过,我不确定VS是否存在这样的编译器标志。
问题出在您的生产线上
Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
这将构造两个对象:一个在=
的右侧,一个在左侧。由于您没有定义复制构造函数,因此左侧的构造函数将简单地复制与右侧构造函数完全相同的指针。您提到的两个析构函数是这两个对象的,而=
左侧的析构函数是导致问题显现的析构函数。
要解决此问题,您可以执行以下操作之一:
正确定义一个复制构造函数,该构造函数不会复制指针,而是分配新指针、复制内部对象等。
更好的方法是将指针替换为现成的类,该类为您执行这些操作,例如
vector
。
如前所述,添加一个复制构造函数/赋值操作,就可以了。 但是,如果您只想解决此问题,则使用指针将很容易。
int main() {
Person *p = new Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
}
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- C++析构函数调用两次,堆栈分配的复合对象
- Qt插槽调用了两次
- 对于优化级别为 0 的 std::vector,析构函数被调用两次
- 为什么转换运算符调用复制构造函数两次,而等效函数只调用它一次
- 调用一个小函数两次(例如在if条件和主体中)比将结果存储在局部变量中更可取
- 为什么这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次
- 插槽调用了两次qt
- 调用某个回调函数两次会导致分段错误:Nan
- 基于 MFC 对话框的应用程序无法调用对话框两次
- 重载运算符 new(),为什么构造函数被调用两次?
- 当 reset() 被unique_ptr调用两次时会发生什么?
- 为什么在C 中超载邮政增量运算符两次调用构造函数
- 现代C++编译器是否能够避免在某些条件下两次调用常量函数
- 如果我对async_read进行两次调用,那么只有在处理完第一次调用之后,才会处理第二次调用,这是否安全
- 如何正确地将对象添加到向量,而无需两次调用析构函数
- boost::asio vs. libpcap:避免两次调用关闭
- 为什么 DNSServiceProcessResult 两次调用我的回调
- 在资源管理器左窗格上两次调用Windows 7外壳扩展dll Initialize方法
- 通过连续两次调用boost::asio::read来检索正确的数据