È necessario implementare TUTTE le funzioni virtuali nelle classi derivate?


91

Può sembrare una domanda semplice, ma non riesco a trovare la risposta da nessun'altra parte.

Supponiamo di avere quanto segue:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Va bene che la classe Derived non implementa la funzione bar ()? E se non TUTTE le mie classi derivate necessitano della funzione bar (), ma alcune lo fanno. Tutte le funzioni virtuali di una classe base astratta devono essere implementate nelle classi derivate o solo quelle che sono puramente virtuali? Grazie

Risposte:


82

Le classi derivate non devono implementare tutte le funzioni virtuali da sole. Hanno solo bisogno di implementare quelli puri . 1 Ciò significa che la Derivedclasse nella domanda è corretta. Si eredita l' barimplementazione dalla sua classe antenato Abstract. (Questo presume che Abstract::barsia implementato da qualche parte. Il codice nella domanda dichiara il metodo, ma non lo definisce. Puoi definirlo in linea come mostra la risposta di Trenki , oppure puoi definirlo separatamente.)


1 E anche in questo caso, solo se verrà istanziata la classe derivata . Se una classe derivata non viene istanziata direttamente, ma esiste solo come classe base di più classi derivate, allora sono quelle classi che sono responsabili dell'implementazione di tutti i loro metodi virtuali puri. La classe "media" nella gerarchia può lasciare alcuni metodi virtuali puri non implementati, proprio come la classe base. Se la classe "media" fa implementare un metodo virtuale puro, poi i suoi discendenti erediteranno che l'attuazione, in modo che non c'è bisogno di re-implementare da soli.


3
E anche questo (implementazione di funzioni virtuali pure) solo se sono destinate ad essere istanziate (in contrasto con l'essere esse stesse una classe base astratta).
Christian Rau

1
Questo è quello che ho pensato. Ma lo sto facendo nel mio progetto e ricevo un errore di collegamento che dice che c'è un "simbolo esterno non risolto" per Derived :: bar (); Ma non ho mai dichiarato bar all'interno di Derived, quindi perché il linker sta cercando il corpo della funzione?
mikestaub

1
@pixelpusher Ovviamente Derived::barha un corpo funzione, quello di Abstract::bar. Quindi sembra che l'unità di traduzione in cui è definita (è addirittura definita da qualche parte?) Non è collegata all'unità di traduzione in cui è chiamata.
Christian Rau

2
@ Rob: They only need to implement the pure ones.è fuorviante. Le classi derivate non devono necessariamente implementare pure funzioni virtuali.
Nawaz

Apprezzo l'aiuto ma @trenki ha colpito nel segno. Sebbene tu abbia avuto ragione anche Christian Rau, in quanto NON è stato definito.
mikestaub

47

Solo i metodi virtuali puri devono essere implementati nelle classi derivate, ma è comunque necessaria una definizione (e non solo una dichiarazione) degli altri metodi virtuali. Se non ne fornisci uno, il linker potrebbe lamentarsi molto bene.

Quindi, solo mettere {}dopo il tuo metodo virtuale opzionale ti dà un'implementazione predefinita vuota:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Tuttavia, un'implementazione predefinita più complessa andrebbe in un file sorgente separato.


7

Lo standard ISO C ++ specifica che tutti i metodi virtuali di una classe che non sono puramente virtuali devono essere definiti.

In poche parole, la regola è:
se la tua classe derivata sovrasta il metodo virtuale della classe Base, dovrebbe fornire anche una definizione, in caso contrario la classe Base dovrebbe fornire la definizione di quel metodo.

Come per la regola precedente nel tuo esempio di codice, virtual void bar();necessita di una definizione nella classe Base.

Riferimento:

Standard C ++ 03: 10.3 Funzioni virtuali [class.virtual]

Una funzione virtuale dichiarata in una classe deve essere definita, o dichiarata pura (10.4) in quella classe, o entrambe; ma non è richiesta alcuna diagnostica (3.2).

Quindi o dovresti rendere la funzione puro virtuale o fornirne una definizione.

Anche le faq di gcc lo documentano:

Lo standard ISO C ++ specifica che tutti i metodi virtuali di una classe che non sono puramente virtuali devono essere definiti, ma non richiede alcuna diagnostica per le violazioni di questa regola [class.virtual]/8. Sulla base di questo presupposto, GCC emetterà solo i costruttori definiti implicitamente, l'operatore di assegnazione, il distruttore e la tabella virtuale di una classe nell'unità di traduzione che definisce il suo primo metodo non in linea.

Pertanto, se non si riesce a definire questo particolare metodo, il linker potrebbe lamentarsi della mancanza di definizioni per simboli apparentemente non correlati. Sfortunatamente, per migliorare questo messaggio di errore, potrebbe essere necessario modificare il linker e non è sempre possibile farlo.

La soluzione è garantire che tutti i metodi virtuali non puri siano definiti. Si noti che un distruttore deve essere definito anche se dichiarato puro virtuale [class.dtor]/7.


3

Sì, va bene ... devi solo implementare qualsiasi funzione virtuale pura per istanziare una classe derivata da una classe base astratta.


1

Sì, è corretto che una classe derivata deve SOVRARRARE la funzione che è Pure Virtual nella classe padre. La classe padre che ha una funzione virtuale pura è chiamata classe astratta solo perché la sua classe figlio deve fornire il proprio corpo della funzione virtuale pura.

Per le funzioni virtuali normali: - Non è necessario sovrascriverle ulteriormente, poiché alcune classi figlio potrebbero avere quella funzione, altre no.

Lo scopo principale del meccanismo della funzione virtuale è il polimorfismo a tempo di esecuzione, se lo scopo principale della funzione virtuale pura (classe astratta) è di rendere obbligatorio avere la stessa funzione con il proprio corpo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.