简体   繁体   中英

[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; in the ~Person, there will be no such pop-up error.

There is dynamic allocation in the Person constructor:

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

So I think I should delete k in the destructor. My question is why the destructor is called twice and how to solve the error?

I am using 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; . At the end, the temporary 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). 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() .

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.

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):

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,

warning: 'class Person' has pointer data members [-Weffc++] but does not override 'Person(const Person&)' [-Weffc++] or 'operator=(const Person&)' [-Weffc++]

I am not sure if such a compiler flag exists for VS though.

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 .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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