2015-05-14 6 views
1

У меня есть такой код, как,[C++] Почему мой деструктор класса называется дважды?

#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!) Выскакивает. И вы также можете увидеть, как деструктор вызывается дважды. Но если я удалю delete [] k; в ~ Person, такой всплывающей ошибки не будет.

Существует динамическое распределение в конструктор Person:

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

Так что я думаю, что я должен удалить к в деструкторе. Мой вопрос, почему деструктор называется дважды и как решить эту ошибку?

Я использую VS 2013.

Спасибо!

+0

возможно дубликат [Что такое Правило трех?] (Http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – Sneftel

+1

использования вектора, чтобы избежать таких проблем, – Gabriel

+0

@Sneftel Да, это вызвано отсутствием конструктора копий – StenSoft

ответ

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++ предупреждает вас об этих проблемах,

предупреждение: 'класс Person' имеет элементы данных указатель [-WeffC++], но не отменяет 'Person (константный Person &)' [-WeffC++] или 'оператор = (Const Person &)' [-WeffC++]

Я не уверен, что если такой флаг компилятор существует VS хотя.

+0

Итак, причина, по которой эта ошибка возникает, заключается в том, что я дважды удаляю указатель, верно? Что делать, если я пишу: "~ Person() { \t \t \t \t если (! К = nullptr) \t \t \t удалить [] к; \t} «Можно ли избежать повторного удаления одного и того же указателя? – Jack

+0

@Jack нет, потому что копия выполняется, когда указатель все еще действителен, до уничтожения временного. – vsoftco

4

Проблема с вашей линии

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

Это создает два объектов: один справа от =, и один слева.Поскольку вы не определили конструктор копирования, то слева будет просто скопировать тот же указатель, что и справа. Два деструктора, которые вы упомянули, относятся к этим двум объектам, а один слева от = - это тот, который вызывает проявление вашей проблемы.

Для решения этой проблемы, вы можете сделать одно из следующих действий:

  1. Правильно определить конструктор копирования, который не будет копировать указатель, а выделить новый указатель, копировать внутренние объекты, и т.д.

  2. Лучшим способом было бы заменить указатель на готовый класс, который сделает это для вас, например, vector.

0

Как уже упоминалось, добавьте операцию конструктора/assignemnt, которая будет в порядке. Но если вы просто хотите решить эту проблему, использовать указатель будет легко.

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