Avviso compilatore GNU & # 8220; classe ha funzioni virtuali ma distruttore non virtuale & # 8221;

StackOverflow https://stackoverflow.com/questions/127426

  •  02-07-2019
  •  | 
  •  

Domanda

Ho definito un'interfaccia in C ++, cioè una classe contenente solo funzioni virtuali pure.

Voglio proibire esplicitamente agli utenti dell'interfaccia di eliminare l'oggetto tramite un puntatore all'interfaccia, quindi ho dichiarato un distruttore protetto e non virtuale per l'interfaccia, qualcosa del tipo:

class ITest{
public:
    virtual void doSomething() = 0;

protected:
    ~ITest(){}
};

void someFunction(ITest * test){
    test->doSomething(); // ok
    // deleting object is not allowed
    // delete test; 
}

Il compilatore GNU mi dà un avvertimento che dice:

  

la classe 'ITest' ha funzioni virtuali ma distruttore non virtuale

Una volta che il distruttore è protetto, qual è la differenza nel fatto che sia virtuale o non virtuale?

Pensi che questo avviso possa essere tranquillamente ignorato o messo a tacere?

È stato utile?

Soluzione

È più o meno un bug nel compilatore. Si noti che nelle versioni più recenti del compilatore questo avviso non viene generato (almeno in 4.3 non lo fa). Avere il distruttore protetto e non virtuale è completamente legittimo nel tuo caso.

Vedi qui per un eccellente articolo di Herb Sutter sull'argomento. Dall'articolo:

Linea guida n. 4: un distruttore di classe base dovrebbe essere pubblico e virtuale, oppure protetto e non virtuale.

Altri suggerimenti

Alcuni dei commenti su questa risposta si riferiscono a una precedente risposta che ho dato, che era errata.

Un distruttore protetto significa che può essere chiamato solo da una classe base, non attraverso la cancellazione. Ciò significa che un ITest * non può essere eliminato direttamente, solo una classe derivata può. La classe derivata potrebbe volere un distruttore virtuale. Non c'è assolutamente nulla di sbagliato nel tuo codice.

Tuttavia, poiché non è possibile disabilitare localmente un avviso in GCC e si dispone già di una vtable, è comunque possibile considerare di rendere virtuale il distruttore. Ti costerà 4 byte per il programma (non per istanza di classe), massimo. Dato che potresti aver dato alla tua classe derivata un dtor virtuale, potresti scoprire che non ti costa nulla.

Se insisti a farlo, vai avanti e passa -Wno-non-virtual-dtor a GCC. Questo avviso non sembra essere attivato per impostazione predefinita, quindi è necessario averlo abilitato con -Wall o -Weffc ++ . Tuttavia, penso che sia un avvertimento utile, perché nella maggior parte dei casi si tratterebbe di un bug.

È una classe di interfaccia, quindi è ragionevole non eliminare oggetti che implementano quell'interfaccia tramite quell'interfaccia. Un caso comune è un'interfaccia per oggetti creati da una fabbrica che dovrebbe essere restituita alla fabbrica. (Avere oggetti contenenti un puntatore alla loro fabbrica potrebbe essere piuttosto costoso).

Concordo con l'osservazione che GCC sta piagnucolando. Invece, dovrebbe semplicemente avvisare quando si elimina un ITest *. Ecco dove sta il vero pericolo.

La mia opinione personale è che avresti fatto la cosa giusta e che il compilatore fosse rotto. Disabiliterei l'avvertimento (localmente nel file che definisce l'interfaccia) se possibile,

Trovo che uso abbastanza questo modello (piccola 'p'). In effetti, trovo che sia più comune per le mie interfacce avere medici protetti che per quelli pubblici. Tuttavia non penso che sia in realtà un idioma così comune (non si parla così tanto) e credo che quando l'avviso è stato aggiunto a GCC fosse appropriato provare a far valere il vecchio dtor deve essere virtuale se tu avere la regola delle funzioni virtuali. Personalmente ho aggiornato la regola a 'dtor deve essere virtuale se si dispone di funzioni virtuali e si desidera che gli utenti siano in grado di eliminare istanze dell'interfaccia attraverso l'interfaccia altrimenti il ??dtor dovrebbe essere protetto e non virtuale' anni fa;)

Se il distruttore è virtuale, si assicura che anche il distruttore della classe base venga chiamato prima di eseguire la pulizia, altrimenti alcune perdite potrebbero derivare da quel codice. Quindi dovresti assicurarti che il programma non abbia tali avvisi (preferibilmente nessun avviso).

Se avessi il codice in uno dei metodi di ITest che hanno provato a eliminare stesso (una cattiva idea, ma legale), il distruttore della classe derivata non sarebbe chiamato. Dovresti comunque rendere virtuale il distruttore, anche se non intendi mai eliminare un'istanza derivata tramite un puntatore di classe base.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top