it-swarm.it

Classe derivata con distruttore non virtuale

Ci sono 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. Avere un distruttore non - virtual di una classe derivata agirà come una forma debole del modificatore Java final?

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

48
Raedwald

Ci sono 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 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 andrà bene. Ma se è così, probabilmente useresti la composizione e non l'ereditarietà.

Avere un distruttore non virtuale di una classe derivata agirà come una forma debole del modificatore finale Java?

No, perché virtualness 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 esiste un meccanismo di linguaggio incorporato in C++ 03 o precedenti per impedire le sottoclassi (*). Il che non è un grosso problema dato che dovresti sempre preferisci la composizione rispetto all'eredità . Cioè, usa l'ereditarietà quando una relazione "è-a" ha più senso di una vera relazione "ha-a".

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

70
In silico

È perfettamente valido avere una classe Base con un distruttore non virtuale se non si chiamerà mai delete su un puntatore di classe Base che punta a un oggetto di classe derivato.

Segui i consigli di Herb Sutter :

N. linea guida: Solo se le classi derivate devono richiamare l'implementazione di base di una funzione virtuale, rendere la funzione virtuale protetta. Solo per il caso speciale del distruttore:

N. linea guida: Un distruttore della classe base deve essere pubblico e virtuale, oppure protetto e non virtuale.


Forse la tua domanda in realtà è:
Il distruttore nella classe Derivato deve essere virtuale se il distruttore della classe Base è virtuale?

La risposta è NO.
Se il distruttore di classe Base è virtuale, il distruttore di classe Derivato è già implicitamente virtuale, non è necessario specificarlo come virtuale esplicitamente.

30
Alok Save

Affrontare l'ultima modifica:

Modifica: sono particolarmente interessato al caso in cui la classe base della classe derivata ha un distruttore virtuale.

In tal caso, il distruttore della classe derivata sarà sarà virtuale, indipendentemente dal fatto che tu aggiunga o meno la parola chiave virtual:

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

Ciò non si limita ai distruttori, se in qualsiasi punto di una gerarchia di tipi un membro di funzione viene dichiarato virtuale, tutte le sostituzioni (e non i sovraccarichi) di quella stessa funzione saranno virtuali, indipendentemente dal fatto che vengano dichiarate come tali o meno. Il bit specifico per i distruttori è che ~derived() sovrascrive virtual ~base() anche se il il nome del membro differisce - questa è l'unica specificità per i distruttori qui.

La tua domanda non è molto chiara. Se la classe base ha un distruttore virtuale, la classe derivata ne avrà una, indipendentemente. Non c'è modo di disattivare la virtualità, una volta 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 della classe base dovrebbe essere virtuale è che è possibile eliminare tramite un puntatore alla classe base. Se la derivazione è privata, non devi preoccuparti di questo, poiché il tuo Derived* non si convertirà in Base*. Altrimenti, ho visto la raccomandazione che se il distruttore della classe base non è virtuale, dovrebbe essere protetto; questo impedisce il caso unico di comportamento indefinito (eliminazione tramite un puntatore alla base) che potrebbe verificarsi. In pratica, tuttavia, molte classi di base (ad esempio std::iterator<>) hanno una semantica in modo tale che non venga nemmeno in mente a nessuno di creare dei puntatori; molto meno eliminano attraverso tali puntatori. Quindi aggiungere la protezione potrebbe essere più uno sforzo di quanto valga la pena.

5
James Kanze

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

3

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

Se non provi mai a eliminare un oggetto tramite un puntatore alla classe base, sarai al sicuro. Questa è un'altra regola che è difficile da applicare e deve essere usata con cura.

A volte non hai alcun controllo sulla classe base e sei costretto a derivarne, anche se il distruttore non è virtuale.

Infine, avere un distruttore non virtuale nella classe base non impone alcuna restrizione alla classe derivata che verrà applicata dal compilatore, quindi non penso che assomigli affatto al finale di Java.

2
Mark Ransom

Si ci sono:

void dothis(Base const&);

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

Qui la classe viene usata polimorficamente, tuttavia delete non viene chiamata, quindi va bene.

Un altro esempio potrebbe essere:

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

perché un shared_ptr è in grado di utilizzare un delete non polimorfico (tramite la cancellazione del tipo).

Ho implementato un avviso in Clang specifico per rilevare la chiamata di delete su classi polimorfiche non finali con distruttori non virtuali, quindi se usi clang -Wdelete-non-virtual-dtor, avviserà in modo specifico per questo caso.

1
Matthieu M.

Un distruttore non virtuale va benissimo purché non si desideri utilizzarlo come puntatore di base per le classi derivate quando si elimina l'oggetto.

Se le sue classi derivate in modo polimorfico, passando e memorizzandolo con un puntatore di base e quindi eliminandolo, la risposta è no, utilizzare un distruttore virtuale.

1
Karoly Horvath

Potresti non voler creare un distruttore virtuale nella classe base? Nessun distruttore fa in questo caso. Se usi il puntatore per basare la classe e crei un distruttore non virtuale nel compilatore genitore uno, genera automaticamente questo avviso! È possibile bloccarlo se si desidera creare la classe genitore finale. Ma la cosa migliore è contrassegnarlo come un finale come:

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 non generano avvisi. La decisione con un distruttore di base virtuale vuoto non è troppo buona ma accettabile. Non è necessario impostare l'attributo final. Ma questa non è la cosa che vuoi. Non devi nemmeno definire un metodo di base virtuale vuoto Foo too Best is:

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

È un codice completamente chiaro per compilatore. Non viene generato alcun avviso e non viene utilizzato alcun codice di riserva.

0
tolya

Sì, No e No.

Il 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, una tabella virtuale se non ne ha già una. Tuttavia, le tabelle virtuali non sono richieste da C++ per eseguire l'ereditarietà.

0

Avere un distruttore non virtuale di una classe derivata agirà come una forma debole del modificatore finale Java?

Affatto. Ecco il mio suggerimento per impedire le sottoclassi in C++ (come il modificatore finale in Java); rendere il distruttore privato in una classe. Quindi è possibile impedire la creazione di sottoclassi da esso

0
SRF