简体   繁体   English

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

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

I have such code like, 我有这样的代码,

#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);
}

Then I run my code, an error(Debug Assertion Failed!) pops up. 然后我运行我的代码,弹出错误(调试断言失败!)。 And you can also see the destructor is called twice. 您还可以看到析构函数被调用两次。 But if I remove the delete [] k; 但是如果我删除delete [] k; in the ~Person, there will be no such pop-up error. 在〜Person中,不会出现此类弹出错误。

There is dynamic allocation in the Person constructor: 在Person构造函数中有动态分配:

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

So I think I should delete k in the destructor. 所以我认为我应该删除析构函数中的k。 My question is why the destructor is called twice and how to solve the error? 我的问题是为什么析构函数被调用两次,以及如何解决该错误?

I am using VS 2013. 我正在使用VS 2013。

Thank you! 谢谢!

The issue is the following. 问题如下。 In the line 在行中

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

you are copy-initializing p , ie you are creating a temporary which is then copied to Person p; 您正在复制初始化p ,即您正在创建一个临时文件,然后将其复制到Person p; . At the end, the temporary Person("Jack", "Bowen", 24, 60, 0.99, 0.98); 最后,临时Person("Jack", "Bowen", 24, 60, 0.99, 0.98); is destroyed, so your Kidney* pointer is dangling, because you didn't implement a copy constructor and the copy is shallow (ie the pointer itself is being copied, not the object it points to). 已被破坏,因此Kidney*指针悬空了,因为您未实现复制构造函数且复制内容较浅(即,指针本身正在复制,而不是其指向的对象)。 And your destructor is called twice because it is first called when the temporary ends its life (at the end of the statement), then again when Person p goes out of scope at the end of main() . 并且您的析构函数被调用两次,因为它在临时变量的生命期结束时(在语句的末尾)首先被调用,然后在Person pmain()的末尾超出范围时再次被调用。

Anytime your class has a pointer, implement its copy constructor and assignment operator . 只要您的类有指针,就实现其复制构造函数和赋值运算符 Or better, use smart pointers like std::shared_ptr , or even better, standard containers that keep track of their dynamic memory like std::vector/std::list etc. 或者更好的方法是使用智能指针,例如std::shared_ptr ,或者更好的标准容器来跟踪其动态内存,例如std::vector/std::list等。

Quick and dirty fix for your code (but really, you must implement the copy constructor since you're going to have all other kinds of issues, eg when returning Person s from a function or when passing Person s by value): 快速而又肮脏的代码修复程序(但实际上,您将必须实现copy构造函数,因为您将遇到其他所有类型的问题,例如,从函数返回Person或按值传递Person时):

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

This avoids any temporary and uses direct initialization. 这样可以避免任何临时情况,并使用直接初始化。

PS: In g++ , compiling with -Weffc++ warns you about these issues, PS:在g++ ,使用-Weffc++编译会警告您这些问题,

warning: 'class Person' has pointer data members [-Weffc++] but does not override 'Person(const Person&)' [-Weffc++] or 'operator=(const Person&)' [-Weffc++] 警告:“类Person”具有指针数据成员[-Weffc ++],但不会覆盖“ Person(const Person&)” [-Weffc ++]或“ operator =(const Person&)” [-Weffc ++]

I am not sure if such a compiler flag exists for VS though. 我不确定VS是否存在这样的编译器标志。

The problem is with your line 问题是你的线

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

This constructs two objects: one to the right of the = , and one to the left. 这构造了两个对象:一个在=的右侧,一个在左侧。 Since you did not define a copy constructor, the one to the left will simply copy the exact same pointer of the one to the right. 由于您没有定义复制构造函数,因此左侧的复制构造函数将简单地将一个相同的指针复制到右侧。 The two destructors you mention are of these two objects, and the one to the left of the = is the one causing the manifestation of your problem. 您提到的两个析构函数都是这两个对象, =左边的一个是导致问题显现的一个。

In order to address this, you can do one of the following: 为了解决这个问题,您可以执行以下操作之一:

  1. Correctly define a copy constructor which will not copy the pointer, but rather allocate a new pointer, copy the internal objects, etc. 正确定义一个复制构造函数,该构造函数将不复制指针,而是分配一个新的指针,复制内部对象,等等。

  2. A better way would be to replace the pointer with a ready-made class which does these things for you, eg, vector . 更好的方法是用现成的类代替指针,该类为您完成这些事情,例如vector

As mentioned before add a copy constructor/assignemnt operation whould be ok. 如前所述,添加复制构造函数/赋值操作应该可以。 But if you just want to resolve this problem, use pointer will be easy. 但是,如果您只是想解决此问题,使用指针将很容易。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM