2010-09-29 19 views
15

Sto ereditando una classe e vorrei chiamare uno dei suoi costruttori. Tuttavia, devo elaborare alcune cose (che non richiedono nulla della classe base) prima di chiamarla. C'è un modo in cui posso chiamarlo in seguito invece di chiamarlo sulla lista di inizializzazione? Credo che questo possa essere fatto in Java e C# ma non sono sicuro di C++.Chiamare un costruttore della classe base in seguito (non nell'elenco di inizializzazione) in C++

I dati che devo passare sul costruttore non possono essere riassegnati in seguito, quindi non posso semplicemente chiamare un costruttore predefinito e inizializzarlo in seguito.

risposta

27

C'è un modo per chiamarlo in seguito anziché richiamarlo nell'elenco di inizializzazione?

No, non è possibile. Il costruttore della classe base deve essere chiamato nell'elenco di inizializzazione e deve essere chiamato per primo.

Infatti, se lo si omette, il compilatore aggiungerà implicitamente la chiamata.

Credo che questo possa essere fatto in Java e C# ma non sono sicuro di C++.

Né C# né Java consentono questo.

Che cosa si può fare, tuttavia, è chiamare un metodo come argomento della chiamata costruttore della classe base. Questo viene poi elaborato prima del costruttore:

class Derived { 
public: 
    Derived() : Base(some_function()) { } 

private: 
    static int some_function() { return 42; } 
}; 
+1

Grazie, non ho pensato solo a chiamare una funzione, quindi funzionerà, ma sarà un po 'complicato perché il costruttore ha 2 parametri. Accetterò questa risposta quando posso (a meno che non ne esca uno migliore, ovviamente). A proposito, hai ragione a non essere in grado di farlo in Java e C#, ho pensato che fosse possibile in Java perché è stato fatto usando 'super (...)' sul corpo del metodo, ma ho notato che ora deve essere la prima linea – placeholder

+4

+1 Dovresti rendere some_function() statico per documentare il fatto che non usa nessuna delle variabili di istanza della classe (che non sono state inizializzate). –

+0

Interessante, mai visto prima. Presumo che la funzione chiamata possa essere una funzione di classe derivata. – PatrickV

-2
struct base{ 
    base(int x){} 
}; 

struct derived : base{ 
    derived(int x) : base(x){} 
}; 

Così costruttori della classe base sono richiamati in C++ dall'elenco inizializzazione della classe derivata.

+4

Questo non è quello che sta chiedendo. –

1

IMHO Non penso sia possibile rinviare chiamando il costruttore della classe base nel modo in cui hai menzionato.

0

Wow, eravamo tutti giovani una volta. Questa risposta non funzionerà, quindi non usarla. Contenuto lasciato per scopi storici.

Se avete il pieno controllo della classe di base, mi consiglia di aggiungere un metodo protetto per fare l'inizializzazione di classe, rendere virtuale e mettere i vostri dettagli di implementazione classe derivata in esso prima di chiamare la sua base:

class Base 
{ 
public: 
    Base() 
    { 
     Initialize(); 
    } 
protected: 
    virtual void Initialize() 
    { 
     //do initialization; 
    } 
}; 

class Derived : Base 
{ 
public: 

    Derived() : Base() 
    { 
    } 
protected: 
    virtual void Initialize() 
    { 
     //Do my initialization 
     //call base 
     Base::Initialize(); 
    } 
}; 
+0

Sfortunatamente, non ho il controllo della classe base. – placeholder

+0

Potrebbe non essere in grado di raggiungere il tuo obiettivo allora. Se pubblichi più codice, potremmo riuscire a trovare qualcosa, ma non l'ho mai visto in C++. – PatrickV

+0

Non mi piace l'idea. La costruzione a due fasi è sempre soggetta a errori. – sbi

11

Come è stato detto da più persone che rispondono, non è possibile ritardare l'invocazione di un costruttore di classe base, ma Konrad has given a good answer che potrebbe risolvere il problema. Tuttavia, questo ha i suoi svantaggi (ad esempio, quando è necessario inizializzare diverse funzioni con valori i cui calcoli condividono i risultati intermedi), quindi, per essere completo, ecco un altro modo di risolvere il problema dell'ordine di inizializzazione fisso, per usando .

Dato l'ordine di inizializzazione fisso, se si ha il controllo sulla classe derivata (e in quale altro modo si farebbe a giocherellare con uno dei suoi operatori?), È possibile intrufolarsi in una base privata in modo che sta per essere inizializzato prima che l'altra base, che possono poi essere inizializzato con i valori già calcolati della base privato:

class my_dirty_little_secret { 
    // friend class the_class; 
public: 
    my_dirty_little_secret(const std::string& str) 
    { 
    // however that calculates x, y, and z from str I wouldn't know 
    } 
    int x; 
    std::string y; 
    float z; 
}; 

class the_class : private my_dirty_little_secret // must be first, see ctor 
       , public the_other_base_class { 
    public: 
    the_class(const std::string str) 
     : my_dirty_little_secret(str) 
     , the_other_base_class(x, y, z) 
    { 
    } 
    // ... 
}; 

La classe my_dirty_little_secret è una base privata in modo che gli utenti di the_class non possono utilizzarlo, tutti i suoi contenuti sono privati, con un'amicizia esplicita che concede solo l'accesso allo the_class. Tuttavia, poiché è elencato per primo nell'elenco della classe base, verrà costruito in modo affidabile prima dello the_other_base_class, quindi qualsiasi cosa calcoli può essere utilizzato per inizializzarlo.
Un bel commento all'elenco delle classi di base si spera che impedisca agli altri di rompere le cose tramite il refactoring.

Problemi correlati