2016-07-07 8 views
0
class A { 
.... 
}; 

class B: public A { 
int x; 
... 
}; 

class C: public A{ 
A * ptr; 
..... 
}; 

class D : public A { 
A* ptr1; 
A* ptr2; 
.... 
}; 

Примечание: Я сделал все конструкторы для B, C, D, просто не включил их там. Итак, A (без полей) - это суперкласс, и у меня есть 3 подкласса (B, C и D), каждый с разными полями.(C++) разрушение цепочки подкласса при вызове деструктора

А является абстрактным классом и его главным выстраивание класса (B, C, D)

Так как я мог бы ситуацию, как

B *x = new B {5}; 
B *x2 = new B {5} 
D * y = new D{x,x2); 

Так что, когда я delete y; я хочу сделать он цепью разрушает 2 указателя двух своих полей, которые (объекты B). Как я могу сделать деструктор для класса D, а затем разрушить цепочку?

Как пример, который я показываю, очень просто, но в других примерах есть все больше и больше слоев. Я хочу убедиться, что все удалено, поэтому утечки памяти не происходят.

Должен ли мой dtor для класса D выглядеть так?

~D(){ 
delete ptr1; 
delete ptr2; 
} 

и для случая класса C я бы просто сделал это?

~C(){ 
    delete ptr; 
    } 

Потому что я сделал это, и он не работает. Я получаю утечки памяти, так что не так?

+3

Вы объявляете своего деструктора виртуальным? – Falmarri

+0

для класса A №. Я думал, что могу переопределить, если бы сделал их в подклассе @Falmarri –

+0

Должно ли это сделать виртуальный дескриптор класса A чисто виртуальным, и мне действительно не нужно делать деструкцию для класса B, он может просто использовать свой по умолчанию один правый? –

ответ

0

Я полагаю, ваш вопрос ошибочен, так как вы говорите, что это цепочка наследования, но на момент написания B, C и D являются inhereting непосредственно из A.

Когда вы new D, что происходит, у вас есть составное объект, состоящий из A, B, C и D - они строятся в порядке наследования.

*y = { 
    // B starts 
    int x; 
    // C starts 
    A * ptr; 
    // D starts 
    A * ptr1; 
    A * ptr2; 
}; 

Обратите внимание, что деструкторы будут называться в обратном порядке, D сначала тогда C и так далее, чтобы очистить память в обратном порядке (чтобы иметь дело с зависимостями от суперкласса). Однако это зависит от того, какой тип объекта вы удаляете.

Если бы мы сделали это:

D * y = new D; 
A * x = y; 
delete x; 

мы эффективно лечить y как A объект, то есть мы ограничены какая A объект знает и может сделать так долго, как мы используем x. Если мы удалим с использованием x, мы собираемся позвонить по номеру ~A(), а не ~D(). Вот почему в этой ситуации деструкторы должны быть виртуальными, что позволяет virtual ~A() позвонить по номеру ~D(), что в в конечном итоге снова вызовет ~A() (настоящая, а не виртуальная отправка).

Независимо от того, являетесь ли вы или не используете полиморфизм/приведение к базовому типу, ваша интуиция правильно - ~D() следует только очистить память, D объекты введенную к классу (ptr1 и ptr2), также для ~C() (только ptr) ,

Утечка из-за нарезки объекта происходит где-то. Например (если вы вынудили это скомпилировать), это хуже, чем приведенный выше пример, потому что данные объекта не будут скопированы (поскольку они не соответствуют определению структуры, не относящемуся к типу объекта, на который ссылаются):

D d; 
C * c = d; // not too bad, we can't refer to D-specific things. 
C c2 = d; // bad, c2 is copied from D up to C superclass and no further. 

Будьте очень осторожны, чтобы вы передавали указатели/ссылки на объекты, которые могут содержать производные типы, и где вы действительно используете виртуальные деструкторы. В противном случае то, что вы делаете, правильно, позволяя деструктору каждого класса очищаться только для этого класса.