69 votes

Est-ce que std::vector.clear() effectue une suppression (libération de la mémoire) sur chaque élément ?

Considérez ce code :

#include <vector>

void Example()
{
    std::vector<TCHAR*> list;
    TCHAR* pLine = new TCHAR[20];
    list.push_back(pLine);
    list.clear();    // is delete called here?
    // is delete pLine; necessary?
}

Fait list.clear() appelez delete sur chaque élément ? En d'autres termes, dois-je libérer la mémoire avant ou après l'opération ? list.clear() ?

56voto

Benoît Points 10901

std::vector appelle le destructeur de chaque élément qu'elle contient lorsque clear() s'appelle. Dans votre cas particulier, il détruit le pointeur mais les objets restent.

Les pointeurs intelligents sont la bonne solution, mais soyez prudent. auto_ptr ne peut pas être utilisé dans les conteneurs std. boost::scoped_ptr ne peut pas non plus. boost::shared_ptr C'est possible, mais cela ne fonctionnera pas dans votre cas car vous n'avez pas de pointeur vers un objet, vous utilisez en fait un tableau. La solution à votre problème est donc d'utiliser boost::shared_array .

Mais je vous suggère d'utiliser std::basic_string<TCHAR> à la place, où vous n'aurez pas à vous occuper de la gestion de la mémoire, tout en bénéficiant des avantages de travailler avec une chaîne de caractères.

0 votes

Bon point, n'hésitez pas à modifier ces détails dans ma réponse... (Je suis un peu rouillé en C++ et je suis embarrassé de ne pas avoir mentionné basic_string !)

0 votes

En ce qui concerne la gestion de la mémoire, je suis un peu un maniaque du contrôle. Je vis au pays du développement embarqué (téléphones portables). Cela me donne la chair de poule d'utiliser une API partiellement gérée. La couche sous l'interface utilisateur est le C ANSI.

0 votes

Que considérez-vous comme "partiellement géré" ? shared_array ou basic_string ? Dans tous les cas, je pense que c'est assez complet ! Si vous voulez contrôler la gestion de la mémoire, je n'utiliserais pas std::vector en premier lieu. Il pourrait réserver de la mémoire sans vous le dire !

42voto

Ruben Bartelink Points 23945

Non (vous devez faire la suppression vous-même à la fin comme vous le suggérez dans votre exemple car la destruction du pointeur chauve ne fait rien). Mais vous pouvez utiliser un pointeur intelligent boost [ou autre idiome basé sur RAII] pour lui faire faire la bonne chose ( auto_ptr ne fonctionnerait pas correctement dans un conteneur car il a un comportement incompatible avec la copie, etc.), mais assurez-vous de comprendre les pièges de ces pointeurs intelligents avant de les utiliser. (Comme Benoit le mentionne, dans ce cas, basic_string est ce que vous recherchez vraiment ici).

Cela dit, il est nécessaire de comprendre les pièges des pointeurs intelligents, car le fait qu'ils prennent en charge la gestion de la mémoire de manière implicite afin que vous n'ayez pas à le faire de manière explicite est bien moins propice aux erreurs.

EDIT : Révision substantielle afin d'inclure les éléments que Benoît a apportés dans sa réponse beaucoup plus approfondie, grâce à l'insistance de l'Earwicker et de James Matta - merci de m'avoir poussé à faire preuve de diligence raisonnable à ce sujet !

4 votes

Auto_ptr ne peut pas être utilisé. auto_ptr se casse quand il est utilisé avec des conteneurs STL.

0 votes

Vous devriez modifier la réponse pour supprimer la référence auto_ptr. Il n'est pas possible de l'utiliser dans un conteneur.

0 votes

JM : je retire le commentaire hors sujet de tout à l'heure, je me prends pour qui ?

9voto

tfinniga Points 3550

Voici un moyen de savoir si ce n'est pas le cas : essayez-le sur une classe qui n'est pas entièrement définie :

#include <vector>
class NotDefined;

void clearVector( std::vector<NotDefined*>& clearme )
{
    clearme.clear();    // is delete called here?
}

Si cet extrait compile, alors il ne peut pas appeler le destructeur, parce que le destructeur n'est pas défini.

8voto

Robert Massaioli Points 6672

Vous pourriez simplement écrire une fonction de modèle simple qui le fait pour vous :

template <class T>
void deleteInVector(vector<T*>* deleteme) {
    while(!deleteme->empty()) {
        delete deleteme->back();
        deleteme->pop_back();
    }

    delete deleteme;
}

Peut-être que quelque chose ici est une mauvaise pratique mais je ne le pense pas. Cela me semble correct, mais les commentaires sont toujours les bienvenus.

1 votes

Cela ne résout pas vraiment le problème. Si list.push_back(pLine) les lancers, la mémoire sera toujours fuie. La bonne chose à faire est d'utiliser std::vector<TCHAR> au lieu de TCHAR* .

2 votes

C'est une mauvaise approche. Des pointeurs partout rendent les programmes extrêmement lents. La manière critique d'écrire des programmes rapides de nos jours est de toujours utiliser une mémoire contiguë, et la meilleure manière est d'utiliser vector<T> directement, T n'étant pas un pointeur.

8voto

Mehrdad Afshari Points 204872

Non. Il ne le fait pas car il n'y a aucune garantie que vous n'utilisiez pas le pointeur ailleurs. Si ce n'était pas une variable de type pointeur, il la libérerait (en appelant le destructeur).

8 votes

@Ignas : Comment std::vector devrait-il savoir comment supprimer le pointeur ? Il peut avoir été alloué avec new, avec malloc ou avec toute autre fonction d'allocation de mémoire (spécifique à l'OS, écrite à la main), il n'y a aucun moyen de le savoir à partir du type.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X