2017-10-19 12 views
2

J'apprends des pointeurs en C++ et j'ai rencontré un problème très étrange. Notre tâche est d'écrire notre propre fonction push_back pour notre propre classe qui contient un tableau d'entiers.La suppression du tableau échoue parfois de temps en temps

J'ai la fonction simple:

void IntRow::push_back(int value){ 
    int *elements_temp = new int[size+1]; 
    for (int i = 0; i <= size; i++){ 
     elements_temp[i] = elements[i]; 
    } 
    elements_temp[size + 1] = value; 
    elements = new int[size+1]; 
    for (int i = 0; i <= size+1; i++){ 
     elements[i] = elements_temp[i]; 
    } 
    delete [] elements_temp; 
    size++; 
} 

Cela échoue cependant MAIS SEULEMENT PARFOIS sur la ligne: delete [] elements_temp;

Avec le message d'erreur suivant:

incorrect checksum for freed object - object was probably modified after being freed. 

Pourquoi est-ce que cela se produit, et pourquoi cela se produit-il seulement parfois?

P.S .: Dois-je réellement libérer elements_temp ou est-il libéré de toute façon après que la fonction existe?

+0

'elements = new int [size + 1];' vous m'avez perdu ici. Pourquoi allouez-vous ** un autre ** tampon? – StoryTeller

+0

Parce que je veux tout copier du tableau temporaire au nouveau tableau d'éléments pour contenir les anciennes valeurs + la nouvelle valeur que nous ne faisons que pousser. –

+2

'elements_temp [size + 1] = value;' est un comportement indéfini. –

Répondre

5

Le comportement de elements_temp[size + 1] = value; est indéfini lorsque vous accédez à un élément en dehors des limites du tableau.

Ce comportement non défini se manifeste par un plantage occasionnel du programme.

Et oui, vous devez appeler explicitement delete[] vous-même sur le pointeur vous revenez de new[]. (Par intérêt, qui appelle delete[] sur elements?)

Vous pouvez également utiliser std::vector<int>.

+1

Votre dernière phrase est un léger euphémisme. –

+0

Mais je commence par déclarer: 'int * elements_temp = new int [taille + 1];' Comment '[taille + 1]' est hors limites? –

+2

Parce que les éléments sont indexés à partir de 0, innit. C++ n'est pas Fortran vous le savez. – Bathsheba

1
int *elements_temp = new int[size+1]; 
for (int i = 0; i <= size; i++){ 
    elements_temp[i] = elements[i]; 
} 
elements_temp[size + 1] = value; 

Vous elements_temp alloué pour stocker size+1 entiers. Les index valides à l'intérieur de ce tableau vont de 0 à size (et nonsize+1), car les index sont basés sur 0 en C++. Donc, vous écrivez en dehors de les limites du tableau dans la dernière ligne ci-dessus. C'est undefined behavior, et c'est probablement la cause de votre problème.

Qu'est-ce que vous voulez sans doute est l'allocation de place pour size+1 entiers, et la copie des entiers précédents au nouveau tableau, puis ajouter le dernier nouvel élément:

// Allocate room for size+1 integers: 
const int newSize = size + 1; 
int* elements_temp = new int[newSize]; 

// Copy the old array data to the new array. 
// Note: i < size, *not* <= ! 
for (int i = 0; i < size; i++) { 
    elements_temp[i] = elements[i]; 
} 

// Write the new item at the end of the array. 
// Note: The index is "size", *not* "size+1" 
elements_temp[size] = value; 

// Update the size data member in your array class to store the new size 
size = newSize; 

De plus, vous devez delete le vieuxelements tableau avant d'attribuer une nouvelle, par exemple:

// Get rid of old array memory 
delete[] elements; 

// If you don't call delete[] above, you will *leak* the old elements array 

// Take ownership of the new array 
elements = elements_temp; 

une meilleure politique d'allocation

Je voudrais ajouter que, l'attribution d'un nouveau tableau chaque fois que vous ajoutez un élément est une politique très inefficace. Une meilleure approche (utilisée par exemplepar le conteneur std::vector standard) est d'allouer de la mémoire à l'avance, créant une sorte de capacité disponible , puis allouer un nouveau tableau et copier les anciennes données au nouveau tableau, que lorsque le vieux vecteur n'a pas plus de place (en d'autres termes, seulement quand il n'y a plus de "capacité" disponible). En fait, les allocations de mémoire dynamique avec new[] sont coûteuses, et il est préférable de les réduire au minimum. (Bien que cette optimisation de la capacité puisse ou non être ce que votre enseignant veut de vous dans ce processus d'apprentissage.)

+0

Merci, c'est assez clair maintenant! Pourquoi attribuez-vous 'const' pour la nouvelle taille? Est-ce mieux que d'utiliser 'size + 1'? –

+0

De rien. Je suis content d'avoir été utile. Je pense que la définition d'une variable en lecture seule (c'est-à-dire une variable que vous ne pouvez pas modifier) ​​avec const et en y stockant la nouvelle valeur de taille rend le code plus clair et moins sujet aux bugs qu'à plusieurs endroits et potentiellement manquer le '+ 1' quelque part ... De toute façon, n'hésitez pas à choisir ce que vous trouvez plus lisible. –

+0

Oh, donc ce n'est pas une question de gains de performance, n'est-ce pas? Ils fonctionnent de la même manière? –