9
votes

C ++ DISRUCTEUR DU DESRUCTEUR AVEC STD :: Vecteur d'objets de classe

Je suis confus sur la façon d'utiliser les destructeurs quand j'ai un std :: vecteur de ma classe.

Donc, si je crée une classe simple comme suit: xxx

puis dans ma fonction principale, je fais ce qui suit: xxx

Je reçois un accident d'exécution dans le destructeur du test lorsque je sors de portée. Pourquoi est-ce et comment puis-je libérer sa mémoire en toute sécurité?


1 commentaires

D'une manière ou d'une autre, mon DVC ++ "m'a sauvé" de ces frales. Ce code [ Ideone.com/ehzbv ] s'exécute absolument bien sur DevC ++ sous Windows. Des idées pourquoi cela pourrait se produire? Ce n'est pas bien. Je veux qu'il écrisse quand il est censé crancer!


4 Réponses :


21
votes

Le problème est que vous ne définissez pas un constructeur de copie pour test . Donc, le compilateur génère un constructeur de copie par défaut pour vous, ce qui vient de copie le contenu de l'objet - dans ce cas le pointeur INT.

Maintenant, lorsque vous repoussez votre objet dans le vecteur, il est implicitement copié avec le constructeur de copie . Ce qui entraîne deux objets pointant vers le même réseau d'INTS! Donc, à la fin, deux destructeurs tentent de supprimer le même tableau - BANG.

Chaque fois que vous définissez une classe qui détient des membres via des pointeurs *, à part le destructeur, vous devez Définir également un constructeur de copie pour cela. Mise à jour: et un opérateur d'affectation, pour la même raison (merci @james: -)

update2: Un moyen trivial pour contourner toutes ces restrictions est de définir une matrice statique au lieu de l'alloué dynamiquement une: xxx

Cependant, la meilleure pratique consiste à utiliser std :: vecteur Au lieu d'un tableau.

* C'est-à-dire, contient des pointeurs aux membres de la sémantique de propriété (grâce à @steve Jessop pour clarification)


3 commentaires

Exactement. Lorsque l'objet de test est ajouté au vecteur, il est copié à l'aide du constructeur de copie. Vous devriez planter si vous essayez d'accéder également à la matrice de l'objet vectoriel.


"Chaque fois que vous définissez une classe contenant des membres du pointeur" - avec la sémantique de propriété. Un pointeur sans propriétaire La sémantique ne nécessite aucun traitement spécial de la classe, mais bien sûr, l'appelant doit s'assurer que l'on reste valide aussi longtemps que l'objet pourrait l'utiliser.


@Steve, pour moi, le confinement impliquait la propriété, mais vous avez raison, j'ai essayé de le rendre plus clair maintenant.



16
votes

Votre problème est ici: xxx pré>

Le test () code> crée un objet temporaire objet code>, qui est ensuite copié sur TOBJ CODE>. À ce stade, TOBJ code> et l'objet temporaire ont gros code> défini sur le tableau. Ensuite, l'objet temporaire est détruit, qui appelle le destructeur et détruit le tableau. Alors quand tobj code> est détruit, il essaie de détruire la matrice déjà détruite. P>

En outre, lorsque TVEC code> est détruit, il détruira ses éléments , donc le tableau déjà détruit sera encore détruit à nouveau. P>

Vous devez définir un constructeur de copie et un opérateur d'affectation afin que lorsqu'un objet test code> est copié, le Big code> Array est copié, ou a une sorte de référence de référence afin qu'elle ne soit détruite tant que tous les propriétaires ne soient détruits. P>

Un correctif facile est de définir votre classe comme celle-ci: P>

class Test
{
private:
 std::vector<int> big;

public:
 Test (): big(10000) {}
};


5 commentaires

Corriger en principe mais pas dans les détails. Test Tobj = Test () ne créera pas d'objet temporaire. Le constructeur de copie est appelé à TVEC.PUSH_BACK (TOBJ);


@shura: expression test () in c ++ crée un objet temporaire, par définition. Les compilateurs sont autorisés à l'optimiser, mais en général, le temporaire est créé.


@Andreyt: Je ne comprends pas votre "par définition". Vous vous dites que les compilateurs sont autorisés à l'optimiser. Et ils font. La définition se brise-t-elle à ce point? Quel compilateur utilisez-vous ce constructeur de copie d'appels pour tester o = test ()?


@shura: Tout d'abord, quand je dis "être définition", je parle spécifiquement de la subexpression Test () . Cette subexpression crée un objet temporaire de type test , par définition donnée en 5.2.3. Deuxièmement, le tout Test TOBJ = Test () en C ++ signifie littéralement "Créer un temporaire et copier-le avec Copy-Constructor". La plupart des compilateurs décideront de l'optimiser, car ils sont explicitement autorisés à, mais c'est une histoire complètement différente. De plus, Tous les compilateurs créeront un temporaire s'ils décident de ne pas optimiser le code (en raison des paramètres du compilateur ou du fait du contexte, etc.).


Que je connaisse un compéritier qui effectue réellement le copie n'a pas d'importance. Ce qui compte, c'est ce que dit la spécification de la langue. Enfin, Tous compilateurs (à moins qu'ils ne soient cassés) nécessiteront que le constructeur de copie accessible doit compiler Test TOBJ = test () , car conceptuellement, le code effectue Copie < / I> d'un temporaire, même si la copie réelle est optimisée. Et je ne comprends pas votre argument sur "briser la définition". Les optimisations toujours violent le comportement abstrait C ++, c'est pourquoi ils sont appelés optimisations. En ce sens, ils font Pause Divers définitions.



0
votes

Sans copie-constructeur, le vecteur créera une copie plate de votre objet. Cela conduit à deux objets de type test référençant le même tableau gros . La première instance supprime la matrice lorsqu'elle est détruite, puis la deuxième instance tente de désir d'un pointeur supprimé, ce qui est un comportement indéfini.


0 commentaires

0
votes
class Test    
{   
private:  
  int big[10000];    
public:

};

2 commentaires

Votre suggestion fonctionnera généralement bien, mais il existe des plates-formes où mettre un int [10000] sur la pile causerait des problèmes. L'attribution d'objets importants du tas est généralement plus sûr.


Il est difficile de savoir exactement quelle plate-forme l'affiche originale utilise, mais peut-être s'il savait vraiment pourquoi il doit utiliser une allocation de tas dans ce cas peut-être qu'il n'aurait peut-être pas fait ses erreurs suivantes. Eh bien, je ne sais pas, peut-être qu'il devrait simplement utiliser un std :: vecteur après tout? Et bien sûr, vous pouvez simplement créer (intelligent) des pointeurs de test pour éviter toutes ces copies inutiles