Il problema di base è qui:
A() {
// I want to call B's foo() here
}
Perché vuoi chiamare B's foo () qui? Come può qualcosa di specifico per B essere rilevante per l'inizializzazione di A, che non ha alcuna idea di B? La risposta indicherà sempre un design scadente.
Una classe di risposte (abbastanza comune, ma apparentemente non pertinente qui) è quando viene utilizzato un metodo virtuale per restituire un valore specifico B che A memorizzerà e / o userà. Il concetto di design scarso qui è che dovrebbe essere usato un metodo virtuale per passare questo valore. Un'alternativa superiore consiste nel passare il valore dalla classe derivata come argomento al costruttore della classe base. In questo modo, la classe base non deve preoccuparsi del modo in cui si ottiene il valore.
Qui, tuttavia, foo () ha una firma di reso nulla. Qualunque cosa faccia, deve farlo nel contesto di B. Quindi, come può influire su A? Potrebbe avere accesso a un membro protetto o chiamare altri metodi di classe base. Ma perché una di queste azioni dovrebbe verificarsi durante l'inizializzazione di A? Perché non durante l'inizializzazione di B?
Il punto di base è che l'inizializzazione di A dovrebbe essere progettata per dipendere solo dai suoi parametri di costruzione. In questo modo, qualsiasi classe derivata può raggiungere la sua personalizzazione invocando il costruttore della classe base dal proprio costruttore con i valori appropriati.
APPENDICE:
Per il modello di metodo del modello, il meglio che puoi fare è rafforzare la disciplina implicita, con un'organizzazione su queste linee:
class Base
{
public:
virtual ~Base(); // for polymorphic destruction
protected: // to ensure invocation from derived classes only
Base()
{
// pre-customization base class actions
}
void finish_init() // virtual methods can be called from here!
{
// post-customization base class actions
}
};
class Derived : public Base
{
public:
Derived() : Base()
{
// derived class customized actions
finish_init();
}
};
Questo non è "a due fasi" in quanto l'inizializzazione della classe derivata avviene interamente all'interno del suo costruttore. Il modo in cui tale lavoro è suddiviso è interno all'implementazione e quindi "unitario" da qualsiasi prospettiva esterna.