2016-09-26 5 views
1

Pour initialiser un élément de référence sans une référence à une entité externe passée au constructeur de la classe contenant, une méthode de fabrication peut être utilisé, comme ceci:Destruct un Initialisé membre de référence de la méthode usine

struct B 
{ 
    B() : a(new_a()) {} 
    // factory 
    a& new_a() 
    { 
     A* a = new A; 
     return *a; 
    } 
    A& a; 
}; 

Cependant , bien sûr, B::a ne sera pas détruit lorsque la vie de B se termine, parce que c'est une référence. Mais il n'y a pas accès à B::a en dehors de B. C'est donc une fuite de mémoire. Pour résoudre ce problème on peut appeler A::~A() de B::~B()

B::~B() {a.~A();} 

Mais j'ai lu manuellement l'appel était un Destructeurs non-non, mettant ainsi un appel à A::~A() dans le destructor de B::~B() tombe à plat, ou est-elle?

Y a-t-il une solution plus propre?

+5

Je pense que vous devez repenser votre conception. Par exemple, * pourquoi * devez-vous avoir une référence? Vous ne pouvez pas avoir une instance réelle (ce qui est vraiment ce que je recommande)? Ou un pointeur (ou plutôt un pointeur intelligent)? –

+0

'return * a;' déréférence le pointeur. 'a' se termine avec une copie du' new'ed 'A', mais le' new'ed 'A' est divulgué. – user4581301

+0

@ user4581301 no. 'new_a' renvoie une référence (en supposant que le type de retour est supposé être' A & 'not' a & ') et qui se lie directement à' B :: a'. Il n'y a pas de copie. –

Répondre

4

delete &a; dans B Le destructeur devrait faire le travail correctement pour éviter une fuite de mémoire.

Je ferais mieux de recommander la fonction d'usine plutôt que de renvoyer un std::unique_ptr<A> cependant. Ou vous utilisez simplement une instance simple de A en tant que membre B.

1

Une solution plus propre serait d'éviter de convertir un pointeur en référence. La référence A& a; membre de données implique que struct B ne possède pas l'objet que a références. Cependant, dans votre cas, cela crée de la confusion.

Vous devez préférer les références (et les pointeurs bruts) pour les relations non propriétaires et les pointeurs intelligents ou les objets de valeur pour posséder des relations.

Par exemple, std::unique_ptr<A> a; membre de données ici montrerait clairement l'intention de propriété, et en bonus, a sera automatiquement libéré lorsque B est détruit. Vous n'avez même pas besoin d'écrire quoi que ce soit dans ~B() destructor.

En conséquence, votre fonction new a() peut être écrit comme ceci:

std::unique_ptr<A> new_a() { return {new A()}; }

En fait, vous n'avez pas besoin d'écrire new_a(). La bibliothèque standard C++ définit déjà une fonction comme celle-ci pour vous: std::make_unique.

+1

'unique_ptr' a un constructeur explicite, donc vous devez faire' return std :: unique_ptr {nouveau A}; '(ou' return std :: make_unique (); 'comme vous l'avez dit). –