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

时间:2015-05-14 22:10:10

标签: c++ class visual-studio-2013 destructor

我有类似的代码,

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

然后我运行我的代码,弹出错误(Debug Assertion Failed!)。你还可以看到析构函数被调用两次。但是如果我删除了〜Person中的delete [] k;,就不会出现弹出错误。

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

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

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

我正在使用VS 2013.

谢谢!

3 个答案:

答案 0 :(得分:4)

问题如下。在行

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()超出范围时再次调用。

任何时候你的类都有一个指针,实现它的复制构造函数和赋值运算符。或者更好的是,使用像std::shared_ptr这样的智能指针,甚至更好的标准容器来跟踪动态内存,如std::vector/std::list等。

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

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

这可以避免任何临时性并使用直接初始化。

PS:在g++中,使用-Weffc++进行编译会警告您这些问题,

  

警告:&#39; class Person&#39;有指针数据成员[-Weffc ++],但不会覆盖&#39; Person(const Person&amp;)&#39; [-Weffc ++]或&#39; operator =(const Person&amp;)&#39; [-Weffc ++]

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

答案 1 :(得分:4)

问题在于您的行

System.Data.Entity.Infrastructure

这构造了两个对象:Person p = Person("Jack", "Bowen", 24, 60, 0.99, 0.98); 右边一个,左边一个。由于您没有定义复制构造函数,因此左侧的复制构造函数将简单地复制右侧的指针。你提到的两个析构函数都属于这两个对象,而=左边的那个是引起你问题表现的那个。

为了解决这个问题,您可以执行以下操作之一:

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

  2. 更好的方法是将指针替换为为您做这些事情的现成类,例如vector

答案 2 :(得分:0)

如前所述,添加复制构造函数/ assignemnt操作就可以了。但是如果你只是想解决这个问题,那么使用指针会很容易。

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