Question

Chaque fois que je dois ajouter dynamiquement objet alloué dans un vecteur je fais que de la manière suivante:

class Foo { ... };

vector<Foo*> v;

v.push_back(new Foo);

// do stuff with Foo in v

// delete all Foo in v

Il a juste travaillé et beaucoup d'autres semblent faire la même chose.

Aujourd'hui, j'ai appris vecteur :: push_back peut lancer une exception. Cela signifie que le code ci-dessus n'est pas sûr d'exception. :-( Alors je suis venu avec une solution:

class Foo { ... };

vector<Foo*> v;
auto_ptr<Foo> p(new Foo);

v.push_back(p.get());
p.release();

// do stuff with Foo in v

// delete all Foo in v

Mais le problème est que la nouvelle façon est bavard, ennuyeux, et je vois faire de personne il. (Du moins pas autour de moi ...)

Dois-je aller avec la nouvelle façon?
Ou, puis-je tenir à l'ancienne?
Ou, est-il une meilleure façon de le faire?

Était-ce utile?

La solution

Si tout ce que vous aimez est sur le point exception à la sécurité de cette opération:

v.reserve(v.size()+1);  // reserve can throw, but that doesn't matter
v.push_back(new Foo);   // new can throw, that doesn't matter either.

La question d'un vecteur ayant la responsabilité de libérer les objets pointés par son contenu est une chose à part, je suis sûr que vous aurez beaucoup de conseils à ce sujet; -)

Edit: hmm, j'allais citer la norme, mais je ne peux pas vraiment trouver la garantie nécessaire. Ce que je suis à la recherche est que push_back ne lancera pas à moins que (a) il doit réallouer (que nous savons que ce sera pas à cause de la capacité), ou (b) un constructeur de T lance (que nous le connaissons a gagné « t puisque T est un type de pointeur). Semble raisonnable, mais raisonnable! = Garanti.

Alors, à moins qu'il ya une réponse bénéfique sur cette question:

Est std :: vector :: push_back autorisé à jeter pour une raison autre que la réaffectation ou la construction a échoué?

ce code dépend de la mise en œuvre ne fait rien trop « d'imagination ». A défaut, votre solution de la question peut être templated jusqu'à:

template <typename T, typename Container>
void push_back_new(Container &c) {
    auto_ptr<T> p(new T);
    c.push_back(p.get());
    p.release();
}

L'utilisation est donc pas trop fastidieux:

struct Bar : Foo { };

vector<Foo*> v;
push_back_new<Foo>(v);
push_back_new<Bar>(v);

Si c'est vraiment une fonction d'usine plutôt que new vous pouvez alors modifier le modèle en conséquence. Le passage d'un grand nombre de différentes listes de paramètres dans différentes situations serait difficile, cependant.

Autres conseils

Votre nouvelle façon est plus exception sûr, mais il y a une raison pour laquelle vous ne le voyez pas fait nulle part ailleurs.

Un vector de pointeurs ne possède que les pointeurs, il ne se prononce pas la propriété des objets pointés. Vous êtes effectivement la propriété à un libérant objet qui ne « veulent » la propriété.

La plupart des gens utilisera un vector de shared_ptr pour exprimer la propriété correctement ou utiliser quelque chose comme boost::ptr_vector. Chacun de ces programmes signifie que vous n'avez pas delete explicitement les objets dont les pointeurs vous stockez qui est sujette aux erreurs et potentiellement exception « dangereux » à d'autres points du programme.

Modifier Vous devez toujours être très prudent avec l'insertion dans ptr_vector. Malheureusement, push_back prendre un pointeur brut fournit la forte garantie qui signifie que soit l'insertion réussit ou (efficace) rien qui se passe, de sorte que l'objet est passé dans ni repris, ni détruite. La version prenant un pointeur intelligent par valeur est définie comme appelant .release() avant d'appeler la version fortement garantie qui a effectivement des moyens qu'il peut fuir.

L'utilisation d'un vector de shared_ptr avec make_shared est beaucoup plus facile à utiliser correctement.

La meilleure façon de le faire est d'utiliser un conteneur de pointeurs intelligents, par exemple, un std::vector<std::shared_ptr<Foo> > ou un std::vector<std::unique_ptr<Foo> > (shared_ptr se trouvent dans Boost et C ++ TR1 ainsi, std::unique_ptr est effectivement limitée à C ++ 0x).

Une autre option consiste à utiliser un conteneur qui possède des objets dynamiques, comme les conteneurs fournis par la bibliothèque Boost pointeur conteneurs.

Comment résiliente un manque de mémoire est votre programme? Si vous tenez vraiment à ce que vous devez être prêt pour new de jeter ainsi. Si vous n'êtes pas allez gérer cela, je ne vous inquiétez pas de sauter à travers les cerceaux de push_back.

Dans le cas général, si vous manquez de mémoire, le programme a déjà des problèmes probablement insurmontables à moins qu'il est spécialement conçu pour fonctionner à jamais dans une empreinte contrainte (systèmes embarqués) - dans ce cas, vous ne devez prendre soin de tous ces cas.

Si cela vaut pour vous, vous pourriez avoir un long examen de code et le cycle nouveau test qui vous attend. Je suppose que vous êtes OK pour suivre la pratique de votre équipe ici, cependant.

Comme d'autres ont en pointe, en utilisant vector pour stocker des pointeurs premières a ses propres problèmes, et il y a une foule de documents sur ce site et dans les autres réponses pour vous diriger vers un modèle plus sûr.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top