/ / c ++ domanda di ereditarietà - c ++, ereditarietà, costruttore esplicito, regola-di-tre

Domanda di ereditarietà c ++ - c ++, ereditarietà, costruttore esplicito, regola-di-tre

Ho una domanda al riguardo:

class A
{
int a;
int* pa;
public:
A(int i):a(i) , pa(new int(a))
{
cout<<"A ctor"<<a<<endl;
}
~A()
{
delete pa;
cout<<"dtorn";
}
int * &get()
{
return pa;
}
};

class B : public A
{
int b;
public:
B (A obj): A(obj) , b(0)
{
cout<<"B ctorn";
}
~B()
{
cout<<"B dtorn";
}
};

int main()
{
int i = 23 ;
A* p = new B(i);
}

Puoi dirmi perché l'ultima riga in main compila? Passo un int in B"s costruttore che si aspetta un A oggetto invece. Credo che il int è tradotto in un A in BCostruttore "s, ma perché?

Grazie in anticipo.

Avri.

risposte:

5 per risposta № 1

Dal momento che non hai dichiarato A costruttore come explicit il compilatore sta creando un'istanza anomala di A utilizzando i e usandolo per inizializzare B esempio. Se non vuoi che il compilatore esegua queste conversioni implicite, dichiara il tuo costruttore come explicit. Quindi otterrai un errore del compilatore.


2 per risposta № 2

Perché A ha un costruttore di parametri singolo che accetta un int e non è contrassegnato explicit puoi convertire implicitamente un int ad A.

Quando lo fai new B(i), perché l'unico costruttore valido per B prende un A, viene effettuato un tentativo di conversione i ad A e costruisci il nuovo B da quello. Questa conversione viene eseguita creando un temporaneo A usando il costruttore che accetta un int.

Quando il B l'oggetto è costruito, la classe base A è una copia costruita dal temporaneo A che significa copiare le variabili membro a e pa dal provvisorio A.

Rigorosamente, perché il costruttore prende un A oggetto per valore, il temporaneo è, concettualmente, copiato di nuovo. Il compilatore può, tuttavia, eliminare il temporaneo costruendo il parametro costruttore B direttamente da i quindi l'effetto potrebbe sembrare solo una singola copia.

Ciò causerà un errore grave perché quando il temporaneo A è distrutto, delete pa causerà l'allocazione dinamica int da distruggere ma la classe base A del nuovo allocato B l'oggetto avrà ancora una copia di questo puntatore che ora non punta più su un oggetto non valido. Se il compilatore non elimina una delle copie, un "doppio libero" avverrà immediatamente.

L'aspetto chiave di A è che ha un distruttore definito dall'utente che esegue un'azione di risorsa (deallocazione). Questo è un forte avvertimento A ha bisogno di un costruttore di copia definito dall'utente e di un operatore di assegnazione della copia poiché la versione generata dal compilatore probabilmente non funzionerà in modo coerente con il design di A.

Questa è conosciuta come la "regola dei tre" che diceche se hai bisogno di una versione definita dall'utente di uno dei distruttori, costruttori di copie o operatori di assegnazione delle copie, probabilmente avrai bisogno di tutte le versioni definite dall'utente.

Dovresti tentare di liberare l'allocazione dinamica B oggetto nel tuo esempio, probabilmente causerebbe un errore "doppio libero". Inoltre, AIl distruttore di s dovrebbe essere contrassegnato come virtual per una cancellazione attraverso un puntatore a A per funzionare correttamente.


1 per risposta № 3

Poiché c'è una conversione da int a A, implicitamente il tuo codice è tradotto in

 A* p = new B(A(i));