6
votes

Supprimer un tableau dans la mauvaise manière

Dupliqué possible: strong>
Comment coupler de nouveaux [] avec Supprimer Peut conduire à une fuite de mémoire uniquement? p>

On m'a toujours dit que ce n'est pas prudent d'appeler Supprimer sur un tableau attribué à nouveau []. Vous devez toujours associer de nouveau avec Supprimer et nouveau [] avec Suppr []. P>

Donc, alors j'ai été surpris de découvrir que le code suivant compile et fonctionne correctement, en mode de débogage et de libération sous VS2008. P >

class CBlah
{
public:
    CBlah() : m_i(0) {}

private:
    int m_i;
};

int _tmain(int argc, _TCHAR* argv[])
{
    for(;;)
    {
        CBlah * p = new CBlah[1000]; // with []
        delete p;                    // no []
    }
    return 0;
}


4 commentaires

Ceci est une erreur logique qui ne peut pas être détectée par les compilateurs. Les bons idées pourraient être capables de repérer ceux-ci, cependant.


Cette question Stackoverflow.com/Questtions/1913343/... explique tous les détails soigneusement. Et oui, c'est une erreur typique.


@Hans Passant: Non, le tableau n'est pas divulgué, c'est UB. J'ai lié à une question qui explique tous les travaux intérieurs de détails.


Utilisez un std :: vecteur , ne supprimez pas les choses manuellement.


9 Réponses :


3
votes

C'est un comportement indéfini et tout est juste dans l'amour, la guerre et le comportement non défini ... :)


0 commentaires

-1
votes

Ceci fonctionne car la bibliothèque d'exécution C ++ particulière utilisée avec utilise le même tas pour les deux NOUVEAU et opérateur nouveau [] . Beaucoup font, mais certains ne le font pas, c'est pourquoi la pratique n'est pas recommandée.

L'autre grande différence est que si cblah avait un destructeur non trivial, le supprimer p; n'appellerait que pour le premier objet de la matrice, alors que < Code> Supprimer [] p; est sûr de l'appeler pour tous les objets.


3 commentaires

En fait, cela ne vous dérangera pas beaucoup: Stackoverflow.com/questions/ 2245196 / C-Urban-mythes / ...


C'est une UB - tout peut arriver.


L'OP indique que certains codes spécifiques "fonctionnent bien" [ou semble]. Même si c'est le cas qu'il y ait une corruption de tas qui ne s'est pas encore arrêté de causer un crash, il est toujours vrai que tout destructeur pour le type supprimé ne serait appelé qu'une fois par la version Supprimer P .



0
votes

Vous pouvez lire ces réponses et liens sur Supprimer et Suppr [] : À propos de Supprimer, de l'opérateur Supprimer, Suppr [], ...


0 commentaires

9
votes

Cela compilera certainement OK, car il n'y a aucune information dans le pointeur (compilation-heure) qui verra si le pointeur pointe du tableau ou de quoi. Par exemple: xxx

maintenant, à propos de fonctionnement OK. Ceci s'appelle un comportement indéfini en C ++, c'est-à-dire qu'il n'y a aucune garantie que va-t-elle arriver - tout peut courir OK, vous pouvez obtenir un segfault, vous pouvez obtenir un comportement erroné, ou votre ordinateur peut décider d'appeler le 911. UB <=> aucune garantie


6 commentaires

Oui. En pratique, cela fonctionne ou cela corrompt le tas. Et la corruption de tas est très mauvais .


Cela compilera n'est pas non plus garanti. Fondamentalement c'est UB. Il n'y a pas de règles.


@CHUBSDAD La compilation à cet égard est garantie (au moins pratiquement), n'est-ce pas? Parce qu'il ne peut jamais savoir si un pointeur pointe vers un tableau ou quoi. En général, Vrai, UB implique une défaillance de la compilation, elle implique tout.


Et la raison de la raison de la corruption du tas est très mauvais est-ce que, quand il se bloque ou sinon case, il le fera à un moment donné distant d'où le bug est ... et Si vous ne récupérez pas cela rapidement, vous passerez des heures ou des jours à essayer de déboguer ce qui peut être parfaitement bon code, ne réalisant pas que le vrai bug peut être dans une partie entièrement différente du programme.


Merci tout le monde ... mais je ne vois toujours pas pourquoi le gestionnaire de mémoire ne peut pas détecter cela au moment de l'exécution, au moins en débogage. Il préfixe chaque bloc attribué avec un en-tête 32 octets (autant que je sache). Cela inclut le nombre d'articles dans le tableau (donc Suppr [] sait combien de fois pour appeler le destructeur) ... de toute façon ... c'est UB, je suis d'accord.


@ben: Comment vous attendez-vous au compilateur de savoir comment traiter les 32 premiers bits? Ces bits pourraient désigner la taille de la matrice ou pourraient être l'INT réel à supprimer. La seule façon dont je peux penser pour le compilateur de savoir est de stocker une table d'adresses renvoyée par New. Certains tout simplement ne font pas ça et je ne les blâme pas. De toute façon, les suppressions explicites devraient être rares car nous devrions utiliser Raii chaque fois que possible



1
votes

Selon MDSN, il traduit delete sur [] lorsque vous essayez de supprimer un tableau. (Voir Il existe , par exemple). Bien que vous ayez un avertissement après la compilation.


0 commentaires

1
votes

La raison pour laquelle le gestionnaire de mémoire de débogage ne prend pas sur cette erreur est probablement parce qu'il n'est pas implémenté au niveau de nouveau / Supprimer, mais au niveau du gestionnaire de mémoire qui est invoqué par Nouveau / Supprimer pour allouer la mémoire requise.
À ce moment-là, la distinction entre la graisse nouvelle et scalaire nouvelle est partie.


1 commentaires

En réalité, dans Visual C ++ Nouveau [] appelle toujours Opérateur Nouveau [] () et DELETE Appelle Toujours Appels Delete () , mais ils sont juste implémentés de la même manière - via malloc () . Noone Soin et pour une raison - attraper toutes les erreurs possibles dans un programme C ++ n'est pas la tâche de la CRT de débogage.



0
votes

Je ne sais pas ce qui vous fait penser qu'il "fonctionne bien". Il compile et complète sans collision. Cela ne signifie pas nécessairement qu'il n'y avait pas de fuite ni de corruption de tas. De plus, si vous en avez eues cette fois, cela ne fait pas nécessairement de faire une chose sûre à faire.

Parfois même un écrasement tampon est quelque chose que vous «sortirez» »car les octets que vous avez écrits n'ont pas été utilisés (peut-être qu'ils sont un rembourrage pour l'alignement). Vous ne devriez toujours pas faire le faire.

Un nouveau t [1] est une forme de nouvelle [] et nécessite toujours une suppression [] même si dans ce cas, il n'y a qu'un seul élément.


0 commentaires

0
votes

point intéressant. Une fois que j'ai examiné un code de code et j'ai essayé de convaincre les programmeurs de réparer la nouvelle désintégration [] Supprimer. Je suis argumenté avec "point 5" de l'efficacité C ++ par Scott Meyers. Cependant, ils se disputaient de "Que voulez-vous, ça fonctionne bien!" et prouvé qu'il n'y avait pas de fuite de mémoire. Cependant, cela n'a fonctionné qu'avec des types de pod. On dirait que la SP essaie de réparer l'inadéquation comme indiqué par Raveline.

Que se passerait-il, si vous avez ajouté un destructeur? P>

#include <iostream>
class CBlah
{
  static int instCnt;
public:
    CBlah() : m_i(0) {++instCnt;}
    ~CBlah()
    {
      std::cout <<"d-tor on "<< instCnt <<" instance."<<std::endl;
      --instCnt;
    }
private:
    int m_i;
};

int CBlah::instCnt=0;

int main()
{
    //for(;;)
    {
        CBlah * p = new CBlah[10]; // with []
        delete p;                    // no []
    }
    return 0;
}


0 commentaires

0
votes

N'oubliez pas que "fonctionne correctement" est dans l'univers du "comportement non défini". Il est tout à fait possible pour une version particulière d'un compilateur particulier à la mettre en œuvre de manière à ce qu'il fonctionne à toutes fins utiles. La chose importante à retenir est que ceci n'est pas garanti et vous ne pouvez jamais vraiment être sûr que cela fonctionne à 100% et que vous ne savez pas que cela fonctionnera avec la prochaine version du compilateur. Il n'est pas non plus portable, car un autre compilateur pourrait fonctionner de manière différente.


0 commentaires