3

Lorsque je compile le code C++/CLI suivant dans VS2008, un avertissement d'analyse de code CA1001 s'affiche.Pourquoi le GC ne dispose-t-il pas automatiquement les membres de ma classe?

ref class A 
{ 
public: 
    A() { m_hwnd = new HWND; } 
    ~A() { this->!A(); } 
protected: 
    !A() { delete m_hwnd; } 
    HWND* m_hwnd; 
}; 

ref class B 
{ 
public: 
    B() { m_a = gcnew A(); } 
protected: 
    A^ m_a; 
}; 

avertissement: CA1001: Microsoft.Design: Mettre en oeuvre IDisposable sur 'B', car il crée membres des types IDisposable suivants: 'A'.

Pour résoudre cet avertissement, je dois ajouter ce code à la classe B:

~B() { delete m_a; } 

Mais je ne comprends pas pourquoi. La classe A implémente IDisposable via son destructeur (et le finaliseur).
Donc, à chaque fois que A récupère des ordures, alors le finaliseur ou le destructeur de A sera appelé, libérant ses ressources non managées.

Pourquoi B doit-il ajouter un destructeur pour appeler 'delete' sur son membre A?
Le GC appellera-t-il seulement le destructeur de A si B appelle explicitement "delete m_a"?


Modifier: il semble que cela fonctionne automatiquement si vous utilisez la méthode « de sucre de syntaxe » de déclarer l'Un membre, comme celui-ci:

ref class B 
{ 
public: 
    B() { } 
protected: 
    A  m_a; 
}; 

mais ce n'est pas toujours possible. Pourquoi le GC n'est-il pas assez intelligent pour disposer automatiquement du pointeur de référence managé de A ^, alors que personne d'autre n'a de pointeur dessus?

Répondre

2

Vous devez utiliser une sémantique de pile pour le membre et ajouter un destructeur à la classe conteneur. Ensuite, le membre sera éliminé. Voir http://msdn.microsoft.com/en-us/library/ms177197.aspx

ref class B 
{ 
public: 
    B() {} 
    ~B() {} 
protected: 
    A m_a; 
}; 

Le membre est encore réf. tapez et est toujours créé sur le tas.

Edit:

dans .net est Jeter tout au plus malheureux, en C# l'ensemble un comportement déterministe est cassé et vous devez être vraiment rigerous avec Éliminez les appels pour obtenir le comportement la plupart des développeurs C++ attendent.

La sémantique de la pile C++/cli améliore les choses. Si vous ne pouvez pas les utiliser, vous êtes de retour à devoir appeler explicitement disposer qui dans C++/cli est représenté par le destructeur. La seule façon d'enchaîner automatiquement les appels aux membres est par le biais de la sémantique de pile si les membres sont des pointeurs gérés normaux comme C# vous devrez chaîner les appels manuellement.

De nombreuses classes peuvent contenir le même pointeur A ^, il n'y a aucun moyen de savoir lequel devrait appeler le destructeur.

Vous recevez l'avertissement car vous avez implémenté le destructeur qui entraîne l'implémentation de IDispose par votre classe. Cela vous donne une chance de nettoyer de manière déterministe.

Le GC seul peut uniquement collecter un objet sans référence et appeler le finaliseur. C'est loin d'être déterministe. Notez que compter sur le finaliseur pour faire le nettoyage devrait être un filet de sécurité seulement comme on peut l'appeler longtemps dans le futur, voire pas du tout.

Je vous recommande d'essayer de concevoir votre code pour permettre le modèle ci-dessus.

+0

Merci, et il est vrai que je pourrais utiliser la syntaxe de la méthode du sucre - en fait, si je fais cela, je n'ai pas besoin d'implémenter un destructeur du tout (enfin, il supprime l'avertissement). Mais dans certaines/la plupart des situations, vous ne pouvez pas utiliser la syntaxe de la méthode du sucre, vous ne pouvez stocker que le pointeur de référence géré. Je veux savoir pourquoi le GC a besoin d'un appel explicite pour supprimer le pointeur de référence géré afin de disposer de l'objet géré. – demoncodemonkey

+0

"Beaucoup de classes peuvent contenir le même pointeur A ^, il n'y a aucun moyen de savoir lequel devrait appeler le destructeur" << Le GC devrait être capable de savoir lequel était le dernier, et quand le dernier est libéré alors il devrait appeler le destructeur. Il semblerait que le GC n'est pas aussi génial qu'il pourrait l'être ... – demoncodemonkey

+0

Je vais vérifier si je peux refactoriser mon code comme vous l'avez dit. – demoncodemonkey

Questions connexes