C ++ Classe padre che chiama una funzione virtuale figlio
-
04-07-2019 - |
Domanda
Voglio che una pura classe genitore virtuale chiami un'implementazione figlio di una funzione in questo modo:
class parent
{
public:
void Read() { //read stuff }
virtual void Process() = 0;
parent()
{
Read();
Process();
}
}
class child : public parent
{
public:
virtual void Process() { //process stuff }
child() : parent() { }
}
int main()
{
child c;
}
Dovrebbe funzionare, ma viene visualizzato un errore non collegato: / Questo utilizza VC ++ 2k3
O non dovrebbe funzionare, sbaglio?
Soluzione
Il titolo del seguente articolo dice tutto: Non chiamare mai funzioni virtuali durante la costruzione o la distruzione .
Altri suggerimenti
In alternativa, crea un metodo factory per creare gli oggetti e rendi privati ??i costruttori, il metodo factory può quindi inizializzare l'oggetto dopo la costruzione.
Funzionerà in generale, ma non per le chiamate all'interno del costruttore della pura classe base virtuale. Al momento della costruzione della classe base, l'override della sottoclasse non esiste, quindi non è possibile chiamarla. Fintanto che lo chiami dopo aver costruito l'intero oggetto, dovrebbe funzionare.
È perché la tua chiamata è nel costruttore. La classe derivata non sarà valida fino a quando il costruttore non sarà completato, quindi il compilatore ha ragione a dartelo per questo.
Esistono due soluzioni:
- Effettua la chiamata a Process () nel costruttore della classe derivata
- definisce un corpo di funzione vuoto per Process come nell'esempio seguente:
class parent
{
public:
void Read() { //read stuff }
virtual void Process() { }
parent()
{
Read();
Process();
}
}
Con un passo in più potresti semplicemente introdurre una sorta di funzione come
class parent
{
public:
void initialize() {
read();
process();
}
}
Devi racchiuderlo in un oggetto che chiama il metodo virtuale dopo che l'oggetto è stato completamente costruito:
class parent
{
public:
void Read() { /*read stuff*/ }
virtual void Process() = 0;
parent()
{
Read();
}
};
class child: public parent
{
public:
virtual void Process() { /*process stuff*/ }
child() : parent() { }
};
template<typename T>
class Processor
{
public:
Processor()
:processorObj() // Pass on any args here
{
processorObj.Process();
}
private:
T processorObj;
};
int main()
{
Processor<child> c;
}
Il problema superficiale è che si chiama una funzione virtuale che non è ancora nota (gli oggetti sono costruiti da Parent a Child, quindi lo sono anche itabili). Il tuo compilatore ti ha avvertito di ciò.
Il problema essenziale , per quanto posso vedere, è che si tenta di riutilizzare la funzionalità per eredità. Questa è quasi sempre una cattiva idea. Un problema di progettazione, per così dire :)
In sostanza, si tenta di creare un'istanza di un modello Metodo modello, per separare il cosa dal quando : prima leggere alcuni dati (in qualche modo), quindi elaborarli (in in qualche modo).
Questo probabilmente funzionerà molto meglio con l'aggregazione: assegnare la funzione Elaborazione al metodo Template da chiamare al momento giusto. Forse puoi anche fare lo stesso per la funzionalità di lettura.
L'aggregazione può essere effettuata in due modi:
- Uso delle funzioni virtuali (ad es. Runtime Binding)
- Uso dei modelli (ad es. Compilare i tempi)
Esempio 1: associazione runtime
class Data {};
class IReader { public: virtual Data read() = 0; };
class IProcessor { public: virtual void process( Data& d) = 0; };
class ReadNProcess {
public:
ReadNProcess( IReader& reader, IProcessor processor ){
processor.process( reader.read() );
}
};
Esempio 2: rilegatura compiletime
template< typename Reader, typename Writer > // definitely could use concepts here :)
class ReadNProcess {
public:
ReadNProcess( Reader& r, Processor& p ) {
p.process( r.read() );
}
};