[c++]为什么我的 class 析构函数被调用了两次?
[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*
指针悬空,因为你没有实现复制构造函数并且复制很浅(即指针本身正在被复制,而不是它指向的对象)。并且你的析构函数被调用了两次,因为它首先在临时结束它的生命(在语句结束时)时被调用,然后在 Person p
在 main()
.[= 结束时超出范围时再次调用。 25=]
任何时候你的 class 有一个指针,实现它的复制构造函数和赋值运算符。或者更好的是,使用像 std::shared_ptr
这样的智能指针,或者更好的是,跟踪它们的动态内存的标准容器,如 std::vector/std::list
等
快速修复您的代码(但实际上,您必须实现复制构造函数,因为您将遇到所有其他类型的问题,例如从函数返回 Person
s 或传递时Person
s 按值):
Person p("Jack", "Bowen", 24, 60, 0.99, 0.98);
这避免了任何临时并使用直接初始化。
PS:在 g++
中,使用 -Weffc++
编译会警告您这些问题,
warning: 'class Person' has pointer data members [-Weffc++] but does not override 'Person(const Person&)' [-Weffc++] or 'operator=(const Person&)' [-Weffc++]
我不确定 VS 是否存在这样的编译器标志。
你的线路有问题
Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
这将构造 两个 对象:一个在 =
的右侧,一个在左侧。由于您没有定义复制构造函数,因此左侧的构造函数将简单地复制与右侧的完全相同的指针。你提到的两个析构函数是这两个对象的, =
左边的那个是导致你的问题出现的那个。
为了解决这个问题,您可以执行以下操作之一:
正确定义一个不拷贝指针的拷贝构造函数,而是分配一个新的指针,拷贝内部对象等
更好的方法是用现成的 class 替换指针,它会为您做这些事情,例如 vector
.
如前所述添加一个副本constructor/assignemnt操作就可以了。但如果你只是想解决这个问题,使用指针会很容易。
int main() {
Person *p = new Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
}
我有这样的代码,
#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*
指针悬空,因为你没有实现复制构造函数并且复制很浅(即指针本身正在被复制,而不是它指向的对象)。并且你的析构函数被调用了两次,因为它首先在临时结束它的生命(在语句结束时)时被调用,然后在 Person p
在 main()
.[= 结束时超出范围时再次调用。 25=]
任何时候你的 class 有一个指针,实现它的复制构造函数和赋值运算符。或者更好的是,使用像 std::shared_ptr
这样的智能指针,或者更好的是,跟踪它们的动态内存的标准容器,如 std::vector/std::list
等
快速修复您的代码(但实际上,您必须实现复制构造函数,因为您将遇到所有其他类型的问题,例如从函数返回 Person
s 或传递时Person
s 按值):
Person p("Jack", "Bowen", 24, 60, 0.99, 0.98);
这避免了任何临时并使用直接初始化。
PS:在 g++
中,使用 -Weffc++
编译会警告您这些问题,
warning: 'class Person' has pointer data members [-Weffc++] but does not override 'Person(const Person&)' [-Weffc++] or 'operator=(const Person&)' [-Weffc++]
我不确定 VS 是否存在这样的编译器标志。
你的线路有问题
Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
这将构造 两个 对象:一个在 =
的右侧,一个在左侧。由于您没有定义复制构造函数,因此左侧的构造函数将简单地复制与右侧的完全相同的指针。你提到的两个析构函数是这两个对象的, =
左边的那个是导致你的问题出现的那个。
为了解决这个问题,您可以执行以下操作之一:
正确定义一个不拷贝指针的拷贝构造函数,而是分配一个新的指针,拷贝内部对象等
更好的方法是用现成的 class 替换指针,它会为您做这些事情,例如
vector
.
如前所述添加一个副本constructor/assignemnt操作就可以了。但如果你只是想解决这个问题,使用指针会很容易。
int main() {
Person *p = new Person("Jack", "Bowen", 24, 60, 0.99, 0.98);
}