2008-12-20 6 views

Répondre

18

Un conteneur STL de pointeur ne nettoiera PAS les données pointées. Il ne nettoiera que l'espace contenant le pointeur. Si vous voulez que le vecteur pour nettoyer les données de pointeur, vous devez utiliser une sorte de mise en œuvre de pointeur intelligent:

{ 
    std::vector<SomeClass*> v1; 
    v1.push_back(new SomeClass()); 

    std::vector<boost::shared_ptr<SomeClass> > v2; 
    boost::shared_ptr<SomeClass> obj(new SomeClass); 
    v2.push_back(obj); 
} 

Lorsque ce champ se termine les deux vecteurs libérera leurs réseaux internes.v1 va fuir la classe SomeClass créée car seul le pointeur est dans le tableau. v2 ne fuira aucune donnée.

0

Comme avec tout autre objet dans le tas, il doit être détruit manuellement (avec suppression).

2

Lorsqu'un vecteur est hors de portée, le compilateur lance un appel à son destructeur qui à son tour libère la mémoire allouée sur le tas.

2

Ceci est un peu abusif. Un vecteur, comme pour la plupart des conteneurs STL, est constitué de deux parties logiques.

  1. l'instance vecteur
  2. la mise en œuvre de la matrice sous-jacente réelle

Bien configurable, # 2 vit presque toujours sur le tas. # 1 cependant peut vivre sur la pile ou le tas, cela dépend juste de la façon dont il est alloué. Par exemple

void foo() { 
    vector<int> v; 
    v.push_back(42); 
} 

Dans ce cas, la partie # 1 vit sur la pile.

Maintenant, comment # 2 est-il détruit? Quand la première partie d'un vecteur est détruite, elle détruit aussi la seconde partie. Ceci est fait en supprimant le tableau sous-jacent dans le destructeur de la classe de vecteurs.

0

Pour répondre à votre première question:

Il n'y a rien de particulier des classes STL (je l'espère). Ils fonctionnent exactement comme les autres classes de modèles. Ainsi, ils ne sont pas automatiquement détruits s'ils sont alloués sur le tas, parce que C++ n'a pas de garbage collection sur eux (à moins que vous le disiez avec une affaire autoptr sophistiquée ou quelque chose). Si vous l'allouez sur la pile (sans nouveau), il sera probablement géré par C++ automatiquement.

Pour votre deuxième question, voici une classe ArrayOfTen très simple pour démontrer les bases de la gestion de la mémoire typique en C++:

/* Holds ten Objects. */ 
class ArrayOfTen { 
    public: 
     ArrayOfTen() { 
      m_data = new Object[10]; 
     } 

     ~ArrayOfTen() { 
      delete[] m_data; 
     } 

     Object &operator[](int index) { 
      /* TODO Range checking */ 
      return m_data[index]; 
     } 

    private: 
     Object *m_data; 

     ArrayOfTen &operator=(const ArrayOfTen &) { } 
}; 

ArrayOfTen myArray; 
myArray[0] = Object("hello world"); // bleh 

Fondamentalement, la classe ArrayOfTen conserve un tableau interne de dix éléments d'objet sur le tas. Quand new [] est appelé dans le constructeur, l'espace pour dix objets est alloué sur le tas, et dix objets sont construits. De manière similaire, lorsque delete [] est appelé dans le destructeur, les dix objets sont déconstruits et la mémoire précédemment allouée est libérée.

Pour la plupart (tous?) Types STL, le redimensionnement est effectué en arrière-plan pour s'assurer qu'il y a suffisamment de jeu de mémoire pour s'adapter à vos éléments. La classe ci-dessus ne prend en charge que des tableaux de dix objets. C'est fondamentalement un typedef très limitatif d'Object [10].

5

Si vous avez un vector<T*>, votre code doit supprimer ces pointeurs avant de supprimer le vecteur: sinon, cette mémoire est divulguée.

Sachez que C++ ne fait pas la collecte des ordures, voici un exemple des raisons pour lesquelles (appologies pour les erreurs de syntaxe, il a été un moment que je l'ai écrit en C++):

typedef vector<T*> vt; 
⋮ 
vt *vt1 = new vt, *vt2 = new vt; 
T* t = new T; 
vt1.push_back(t); 
vt2.push_back(t); 
⋮ 
delete vt1; 

La dernière ligne (delete vt1;) ne devrait pas effacer le pointeur qu'il contient; après tout, c'est aussi dans vt2. Donc ce n'est pas le cas. Et non plus la suppression de vt2.

(Si vous voulez un type de vecteur qui supprime des pointeurs sur détruire, un tel type peut bien sûr être écrit. Probablement a été. Mais méfiez-vous des pointeurs delete'ing que quelqu'un d'autre tient toujours une copie.)

+1

Oui, il a: boost :: ptr_vector. (voir boites boost ptr) –

2

Si vous stockez des pointeurs dans des classes de conteneur STL, vous devez les supprimer manuellement avant que l'objet ne soit détruit. Cela peut être fait en bouclant à travers le conteneur entier et en supprimant chaque élément, ou en utilisant une classe de pointeur intelligent. Cependant, n'utilisez pas auto_ptr car cela ne fonctionne tout simplement pas avec les conteneurs. Un bon effet secondaire de ceci est que vous pouvez garder plusieurs conteneurs de pointeurs dans votre programme, mais que ces objets appartiennent uniquement à l'un de ces conteneurs, et vous n'avez besoin que de nettoyer ce conteneur.

La meilleure façon de supprimer les pointeurs serait de faire:

for (ContainerType::iterator it(container.begin()); it != container.end(); ++it) 
{ 
    delete (*it); 
} 
+0

Ceci n'est pas conseillé (sauf s'il fait partie du destructeur) car ce n'est pas une exception. Vous devez utiliser un conteneur qui comprend qu'il contient un pointeur. –

+0

Comment n'est-ce pas une exception sûre? Si ContainerType est quelque chose comme std :: vector par exemple, alors ce code est parfaitement valide pour mettre à la fois un destructeur et une méthode "clear" ou "reset". – Daemin

0

Pour supprimer les éléments pointés, je l'ai écrit un foncteur simple:

template<typename T> 
struct Delete { 
    void operator()(T* p) const { delete p; } 
}; 

std::vector<MyType> v; 
// .... 
std::for_each(v.begin(), v.end(), Delete<MyType>()); 

Mais vous devriez fallback sur des pointeurs partagés lorsque le contenu du vecteur doit être ... ehm ... partagé. Oui.

0

Les conteneurs standard STL placer une copie de l'objet original dans le récipient, à l'aide du constructeur de copie. Lorsque le conteneur est détruit, le destructeur de chaque objet dans le conteneur est également appelé pour détruire l'objet en toute sécurité.

Pointeurs sont traités de la même façon.
La chose est les pointeurs sont des données POD. Le constructeur de copie pour un pointeur est juste pour copier l'adresse et les données POD n'ont aucun destructeur. Si vous souhaitez que le conteneur gère un pointeur, vous devez:

  • Utilisez un conteneur de pointeurs intelligents. (par exemple un pointeur partagé).
  • Utilisez un conteneur boost ptr.

Je préfère le conteneur pointeur:
Les conteneurs de pointeur sont les mêmes que les conteneurs STL, sauf que vous mettez des pointeurs en eux, mais le conteneur prend alors la propriété de l'objet les points de pointeur à et se désaffecter ainsi l'objet (généralement en appelant delete) lorsque le conteneur est détruit. Lorsque vous accédez aux membres d'un conteneur ptr, ils sont renvoyés par référence afin qu'ils se comportent comme un conteneur standard à utiliser dans les algorithmes standard.

int main() 
{ 
    boost::ptr_vector<int> data; 

    data.push_back(new int(5)); 
    data.push_back(new int(6)); 

    std::cout << data[0] << "\n"; // Prints 5. 
    std::cout << data[1] << "\n"; // Prints 6. 


} // data deallocated. 
    // This will also de-allocate all pointers that it contains. 
    // by calling delete on the pointers. Therefore this will not leak. 

Il faut également souligner que les pointeurs intelligents dans un conteneur est une alternative valable, malheureusement std :: auto_ptr <> est pas un choix valide de pointeur intelligent pour cette situation. En effet, les conteneurs STL supposent que les objets qu'ils contiennent sont copiables, malheureusement std :: auto_ptr <> n'est pas copiable dans le sens traditionnel car il détruit la valeur d'origine sur la copie et donc la source de la copie ne peut pas être copiée être const.

0

conteneurs STL sont comme tous les autres objets, si vous instancier un il est créé sur la pile:

std::vector<int> vec(10); 

Comme toute autre variable de la pile, il ne vit que dans le cadre de la fonction qu'elle est définie dans et n'a pas besoin d'être supprimé manuellement. Le destructeur de conteneurs STL appellera le destructeur de tous les éléments du conteneur.

Garder les pointeurs dans un conteneur est un problème. Puisque les pointeurs n'ont pas de destructeur, je dirais que vous ne voudrez jamais mettre des pointeurs bruts dans un conteneur STL. Faire ceci d'une manière sûre d'exception sera très difficile, vous devriez jeter votre code avec des blocs try {} finally {} pour vous assurer que les pointeurs contenus sont toujours désalloués.

Alors, que devriez-vous mettre dans des conteneurs plutôt que des pointeurs bruts? +1 jmucchiello pour faire apparaître boost :: shared_ptr. boost :: shared_ptr est sûr à utiliser dans les conteneurs STL (contrairement à std :: auto_ptr). Il utilise un mécanisme de comptage de références simple, et est sûr à utiliser pour les structures de données qui ne contiennent pas de cycles. De quoi auriez-vous besoin pour les structures de données qui contiennent des cycles?

Dans ce cas, vous voudrez probablement passer à la récupération des ordures, ce qui signifie essentiellement utiliser un langage différent comme Java. Mais c'est une autre discussion. ;)

Questions connexes