2011-09-13 15 views
34

Esistono circostanze in cui è legittimo che una classe derivata abbia un distruttore non virtual? Un distruttore non virtual indica che una classe non deve essere utilizzata come classe base. Un distruttore non virtual di una classe derivata si comporta come una forma debole del modificatore Java final?Classe derivata con distruttore non virtuale

Sono particolarmente interessato al caso in cui la classe base della classe derivata ha un distruttore virtual.

risposta

51

Esistono circostanze in cui è legittimo che una classe derivata abbia un distruttore non virtuale?

Sì.

Un distruttore non virtuale indica che una classe non deve essere utilizzata come una classe base.

Non proprio; un distruttore non virtuale indica che l'eliminazione di un'istanza di derived tramite un puntatore base non funzionerà. Per esempio:

class Base {}; 
class Derived : public Base {}; 

Base* b = new Derived; 
delete b; // Does not call Derived's destructor! 

Se non fai delete nel modo sopra, allora sarà bene. Ma se questo è il caso, probabilmente useresti la composizione e non l'ereditarietà.

Will avere un distruttore non virtuale di un atto di classe derivata come una forma debole del modificatore finale di Java?

No, perché virtual -ness si propaga alle classi derivate.

class Base 
{ 
public: 
    virtual ~Base() {} 
    virtual void Foo() {}; 
}; 

class Derived : public Base 
{ 
public: 
    ~Derived() {} // Will also be virtual 
    void Foo() {}; // Will also be virtual 
}; 

Non c'è un meccanismo incorporato il linguaggio in C++ 03 o precedente per impedire sottoclassi (*). Il che non è un problema in ogni caso, dal momento che si dovrebbe sempre prefer composition over inheritance. Cioè, usa l'ereditarietà quando una relazione "è-a" ha più senso di una vera relazione "ha-a".

(*) modificatore 'finale' è stato introdotto in C++ 11

+17

"' virtual'-ness si propaga alle classi derivate. " Lo fa? [consulta lo standard]. 12.4.7: "Se una classe ha una classe base con un distruttore virtuale, il suo distruttore (se dichiarato dall'utente o implicitamente) è virtuale. – Raedwald

+2

Su una nota correlata, se si ha una classe Base non virtuale, e hai alcuni metodi virtuali nella tua classe derivata, quindi 'Base * b = new Derived(); delete b;' sarà un comportamento indefinito e probabilmente bloccherà il tuo programma. Sembra abbastanza sicuro, ma non lo è. (È perché 'b 'non punta al 'inizio' dell'oggetto' Derivato' - sarà compensato dallo spazio necessario per il vtable, quindi 'delete' non opererà esattamente sullo stesso indirizzo di' new', e quindi non è un indirizzo valido da liberare Se hai intenzione di avere metodi virtuali da qualche parte, allora metti un virtual dator nella base: –

0

sì, no e no

distruttore virtuale non ha nulla a che fare con la capacità della classe di essere una base o una classe derivata. È legittimo come entrambi.

Tuttavia, ci sono alcuni motivi per rendere virtuali i distruttori. Vedi qui: http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors. Questo rende una classe, tra le altre cose, avere una tabella virtuale se non ne ha già una. Tuttavia, le tabelle virtuali non sono richieste da C++ per fare ereditarietà.

+0

Wikipedia è ottimo per un punto di partenza nella ricerca e non deve essere utilizzato come fonte di riferimento. http://www.wikihow.com/Cite-a-Wikipedia-Article-in-MLA-Format –

1

un distruttore non virtuale è perfettamente bene fino a quando non si vuole usare come puntatore base per le classi derivate quando cancellando l'oggetto.

Se voi le sue classi derivate in modo polimorfo, passando e la memorizzazione con un puntatore base e poi eliminarlo allora la risposta è no, utilizzare un distruttore virtuale.

+0

Il mio commento su: * Un distruttore non virtuale è perfettamente a posto finché tu non 't voglio eliminare l'oggetto con un puntatore del tipo uno delle sue superclassi. * ... Anche se il distruttore è virtuale, d eliminando l'oggetto usando un puntatore di tipo superclasse, si invocherà un comportamento indefinito SE il distruttore della superclasse non è virtuale. – Nawaz

+0

Ahhm giusto, mi dispiace & thx –

+0

Questo non è ancora corretto. Devi modificare la vecchia frase. – Nawaz

24

E 'perfettamente valido per avere una classe base con un distruttore non virtuale se si sono mai andare a chiamare cancellare su una classe di base puntatore che punta a un oggetto della classe derivata.

Follow Herb Sutter's Advice:

Linea guida #: Solo se le classi derivate devono richiamare l'implementazione di base di una funzione virtuale, rendere la funzione virtuale protetto. Per il caso speciale di solo il distruttore:

Linea guida #: Una classe distruttore di base dovrebbe essere sia pubblico e virtuale, o protetti e non virtuale.


Forse la tua domanda in realtà è:
fa Destructor in Deriva classe deve essere virtuale se la classe Base Destructor è virtuale?

La risposta è NO.
Se la classe Base distruttore è virtuale, allora il distruttore della classe derivata viene implicitamente virtuali già, non è necessario specificare come virtuale in modo esplicito.

+0

"Il distruttore nella classe derivata deve essere virtuale se il classificatore di base è virtuale?" Sì, questa è davvero la mia domanda. – Raedwald

+0

Se nessun dato è allocato (stack, heap) nella classe derivata (cioè la dimensione di base e gli oggetti derivati ​​sono uguali), è comunque possibile utilizzare il distruttore di base non virtuale di base. Questa può essere una soluzione per "typedef" che devono essere espressi esplicitamente: una classe base vettoriale (di alcune API non modificabili), con sottoclassi point, normal e direction con costruttori espliciti. – Matthias

2

Dipende dallo scopo della classe. A volte è una buona pratica per rendere il vostro distruttore protetto, ma non virtuale - che in fondo dice: "Non eliminare un oggetto di classe derivata tramite un puntatore base-tipo"

1

Sì, ci sono:

void dothis(Base const&); 

void foo() { 
    Derived d; 
    tothis(d); 
} 

Qui la classe viene utilizzata in modo polimorfico, ma non viene chiamato delete, quindi va bene.

Un altro esempio sarebbe:

std::shared_ptr<Base> create() { return std::shared_ptr<Base>(new Derived); } 

perché un shared_ptr è in grado di utilizzare un non-polimorfica delete (tipo passante cancellazione).

ho implementato un avvertimento in Clang specificamente per rilevare la chiamata di delete sulle classi non definitive polimorfici con distruttori non virtuali, quindi se si utilizza clang -Wdelete-non-virtual-dtor, si verrà avvertiti appositamente per questo caso.

0

Se la classe derivata non aggiunge alcun membro dati alla classe base e ha un corpo distruttore vuoto, non importa se il distruttore è virtuale o meno - tutto ciò che il distruttore derivato farà è chiamare il base uno comunque. Non è raccomandato perché è troppo facile per qualcuno venire e modificare la classe senza essere a conoscenza di queste restrizioni.

Se non si tenta mai di eliminare un oggetto attraverso un puntatore alla classe base, si è sicuri. Questa è un'altra regola che è difficile da applicare e dovrebbe essere utilizzata con cura.

A volte non hanno alcun controllo sulla classe di base e si è costretti a ricavare da esso, anche se il distruttore, non è virtuale.

Infine, avendo un distruttore non virtuale nella classe base non impone alcuna restrizione alla classe derivata che verrà applicata dal compilatore, quindi non credo che assomiglia a Java di finale a tutti.

3

La tua domanda non è chiara. Se la classe base ha un distruttore virtuale , la classe derivata ne avrà una, a prescindere. Non è possibile che disattivi la virtualità, una volta che è stata dichiarata.

E ci sono certamente casi in cui ha senso derivare da una classe che non ha un distruttore virtuale. Il motivo per cui il distruttore di classe di base deve essere virtuale è in modo che sia possibile eliminare tramite un puntatore la classe di base. Se la derivazione è privata, non è necessario che si preoccupi di ciò, dal momento che lo Derived* non verrà convertito in Base*. Altrimenti, ho visto la raccomandazione che se il distruttore della classe base non è virtuale, dovrebbe essere protetto; ciò impedisce il caso di comportamento non definito (eliminazione tramite un puntatore alla base) che potrebbe verificarsi . In pratica, tuttavia, molte classi base (ad esempio std::iterator<>) hanno semantica tale che non si verifica addirittura chiunque di creare puntatori a loro; molto meno cancellare attraverso tali puntatori . Quindi aggiungere la protezione potrebbe essere più impegnativo di quanto valga.

+0

"La tua domanda non è chiara" +1 –

8

Addresssing l'ultima modifica:

Edit: Sono particolarmente interessato nel caso in cui la classe di base della classe derivata ha un distruttore virtuale.

In tal caso, il distruttore della classe derivata sarà essere virtuale, indipendentemente dal fatto che si aggiunge il virtual parola chiave o no:

struct base { 
    virtual ~base() {}  // destructor is virtual 
}; 
struct derived : base { 
    ~derived() {}   // destructor is also virtual, because it is virtual in base 
}; 

Questo non è limitato ai distruttori, se in qualsiasi puntare in una gerarchia di tipi un membro della funzione è dichiarato virtuale, tutti gli overrides (non gli overload) di quella stessa funzione saranno virtuali, indipendentemente dal fatto che siano dichiarati tali o meno. Il bit specifico per distruttori è che ~derived()le sostituzionivirtual ~base() anche se il nome degli Stati differisce --that è l'unica specificità per distruttori qui.

0

Avrà un distruttore non virtuale di una classe derivata che si comporta come una forma debole del modificatore finale Java?

Niente affatto. Ecco il mio suggerimento per prevenire le sottoclassi in C++ (come il modificatore finale in Java); rendere privato il distruttore in una classe. Poi si può evitare facendo sottoclassi da esso

0

Non si può decidere di creare distruttore virtuale nella classe di base? Nessun distruttore in questo caso. Se si utilizza il puntatore alla classe base e si crea un distruttore non virtuale nel compilatore padre uno genera automaticamente questo avviso! Puoi bloccarlo se vuoi creare la classe genitore finale. Ma la cosa migliore è contrassegnarlo come finale:

class Base{ 
public: 
    //No virtual destructor defined 
    virtual void Foo() {}; 
}; 

class Derived final : public Base{ 
public: 
    ~Derived() {} // define some non-virtual destructor 
    void Foo() {}; // Will also be virtual 
}; 

In questo caso il compilatore sa cosa vuoi e nessun avviso genera. La decisione con il distruttore di base virtuale vuoto non è troppo buona ma accettabile. Non è necessario impostare l'attributo finale. Ma questa non è la cosa voi vogliono, inoltre, non è necessario definire il metodo base virtuale vuoto troppo Foo migliore è:

class Base{ 
public: 
    //No virtual destructor defined 
    virtual void Foo() = 0; // abstract method 
}; 
class Derived final : public Base{ 
public: 
    ~Derived() {} // define some non-virtual destructor 
    void Foo() {}; // Will also be virtual 
}; 

E 'chiaro il codice completo per compilator. Nessun avviso generato e nessun codice di riserva utilizzato.

Problemi correlati