4

J'ai rencontré un comportement étrange dans le compilateur Visual Studio 2010 C++. code suivant compile, mais lance "l'affirmation de débogage a échoué" après l'exécution avec message:Erreur d'exécution Visual Studio 2010 C++

"_BLOCK_TYPE_IS_VALID (pHead-> nBlockUse)"

Compile et le bon déroulement du CCAG. Est-ce ma faute?

#include <iostream> 
#include <vector> 


using namespace std; 

typedef unsigned int uint; 


class Foo { 
    vector<int*> coll; 
public: 

    void add(int* item) { 
     coll.push_back(item); 
    } 

    ~Foo() { 
     for (uint i = 0; i < coll.size(); ++i) { 
      delete coll[i]; 
      coll[i] = NULL; 
     } 
    } 
}; 

int main() 
{ 
    Foo foo; 
    foo.add(new int(4)); 
    Foo bar = foo; 

    return 0; 
} 
+1

Cette erreur est générée par l'exécution de débogage VS; Si vous l'aviez compilé, vous n'obtiendriez pas l'erreur, mais vous risqueriez un crash. Si par GCC vous vouliez dire GCC sous Linux, vous pouvez voir la même erreur en exécutant votre code sous valgrind. – Rup

Répondre

7

Vous n'avez pas implémenté un constructeur de copie et un opérateur d'affectation de copie (voir la règle de trois). Cela entraîne une copie superficielle des pointeurs dans votre vecteur, provoquant une double suppression et l'assertion. EDIT: Double delete est un comportement indéfini, donc VS et gcc sont corrects ici, ils sont autorisés à faire ce qu'ils veulent.

Généralement, lorsque vous implémentez un destructeur avec un comportement non trivial, vous devez également écrire ou désactiver la construction de copie et l'affectation de copie.

Cependant dans votre cas avez-vous vraiment besoin de stocker les articles par pointeur? Sinon, il suffit de les stocker en valeur et cela réglerait le problème. Sinon, si vous avez besoin de pointeurs, utilisez shared_ptr (à partir de votre compilateur ou boost) au lieu de pointeurs bruts pour éviter d'avoir à écrire vos propres méthodes de destructeur/copie.

EDIT: Une autre remarque sur votre interface: Les interfaces comme celle-ci qui transfèrent la propriété des pointeurs passés peuvent causer de la confusion chez les personnes utilisant votre classe. Si quelqu'un passe dans l'adresse d'un int non affecté sur le tas, votre destructeur échouera toujours. Mieux vaut soit accepter par valeur si possible, ou cloner l'élément passé en faisant votre propre appel à new dans la fonction add.

+0

Je ne recommanderais pas aveuglément 'shared_ptr', leur comportement est compliqué (références fortes/faibles) et il est donc préférable de les éviter lorsque d'autres solutions peuvent être utilisées. Ici, je peux voir 'std :: vector ', 'std :: vector >' et 'boost :: ptr_vector ' en tant que prétendants supérieurs. –

+0

Merci pour vos réponses! – user742010

3

vous supprimez l'article deux fois, parce que la ligne

Foo bar = foo; 

Invoque le constructeur de copie par défaut, ce qui fait double emploi avec la ItemPointer, plutôt que d'allouer et de copie des données.

2

Le problème est à la fois le bar et l'élément foo du membre est identique. Lorsque foo est hors de portée, son destructeur est appelé, ce qui libère le pointeur en laissant l'élément vectoriel bar suspendu. bar destructor essaie de libérer son élément vectoriel qui a été laissé en suspens et vous cause l'erreur d'exécution. Vous devriez écrire un constructeur de copie.

Foo bar = foo; // Invokes default copy constructor. 

Edit 1: Regardez ce fil pour savoir sur Rule of three

0

La solution plus simple est de ne pas utiliser ici int* en premier lieu. En général, essayez d'éviter new en général.

Si vous ne le pouvez pas, utilisez un gestionnaire intelligent (tel que std::unique_ptr) pour gérer le nettoyage de la mémoire. En tout cas, si vous appelez delete manuellement, vous le faites mal.Remarque: n'appelant pas delete et laissant la fuite de mémoire se produire également