2008-09-26 8 views
150

Conception d'un nouveau système à partir de zéro. J'utiliserai le STL pour stocker des listes et des cartes de certains objets de longue durée. Question: Dois-je m'assurer que mes objets ont des constructeurs de copie et stocker des copies d'objets dans mes conteneurs STL, ou est-il généralement préférable de gérer moi-même la portée & et de stocker simplement les pointeurs sur ces objets dans mes conteneurs STL? Je me rends compte que c'est un peu court sur les détails, mais je cherche la meilleure réponse "théorique" si elle existe, puisque je sais que ces deux solutions sont possibles.Dois-je stocker des objets entiers ou des pointeurs sur des objets dans des conteneurs?

Deux inconvénients très évidents à jouer avec des pointeurs: 1) Je dois gérer moi-même l'allocation/désallocation de ces objets dans une portée au-delà de la STL. 2) Je ne peux pas créer un objet temp sur la pile et l'ajouter à mes conteneurs.

Y at-il autre chose qui me manque?

+31

dieu j'aime ce site, c'est la question exacte que je pensais aujourd'hui ... merci de faire le travail de me le demander :-) – eviljack

+2

une autre chose intéressante est que nous devrions vérifier si le pointeur a été effectivement ajouté à la collection et si ce n'est pas le cas, nous devrions appeler delete pour éviter les fuites de mémoire ... if ((set.insert (pointeur)) second = false) {supprimer le pointeur;} – javapowered

Répondre

61

Puisque les gens sont dans le sonnent efficency de l'utilisation des pointeurs. Si vous envisagez d'utiliser un std :: vector et si les mises à jour sont peu nombreuses et que vous itérez souvent sur votre collection et qu'il s'agit d'un objet non polymorphe stockant des objets, les copies seront plus efficaces car vous obtiendrez une meilleure localisation. référence. Otoh, si les mises à jour sont courantes, le stockage des pointeurs économisera les coûts de copie/relocalisation.

+12

Bon point sur la localisation du cache. +1 – Branan

+6

En termes de localisation de cache, le stockage de pointeurs dans un vecteur peut être efficace s'il est utilisé avec un allocateur personnalisé pour les pointes. L'allocateur personnalisé doit prendre en charge la localisation du cache, par exemple en utilisant le placement nouveau (voir http://en.wikipedia.org/wiki/Placement_syntax#Custom_allocators). –

34

Si vous stockez des objets polymorphiques, vous devez toujours utiliser une collection de pointeurs de classe de base. Si vous envisagez de stocker différents types dérivés dans votre collection, vous devez stocker des pointeurs ou être mangé par le démon de découpage.

+0

J'ai adoré le deamon tranchage! – idichekop

3

Vous semblez avoir une bonne compréhension de la différence. Si les objets sont petits et faciles à copier, alors rangez-les. Sinon, je penserais à stocker des pointeurs intelligents (pas auto_ptr, un pointeur intelligent de comptage ref) à ceux que vous allouez sur le tas. Évidemment, si vous optez pour des pointeurs intelligents, vous ne pouvez pas stocker les objets alloués de la pile temporaire (comme vous l'avez dit).

@Torbjörn fait un bon point sur le découpage en tranches.

+1

Oh, et * jamais * * jamais * * jamais * créer une collection de auto_ptr –

+0

À droite, auto_ptr n'est pas un pointeur intelligent - il ne compte pas ref. –

+0

auto_ptr n'a pas non plus de sémantique de copie non-destructive. L'assignation d'un auto_ptr de 'one' à' another' va libérer la référence de 'one' et changer' one'. –

17

Pourquoi ne pas obtenir le meilleur des deux mondes: faire un conteneur de pointeurs intelligents (comme boost::shared_ptr ou std::shared_ptr). Vous n'avez pas besoin de gérer la mémoire et vous n'avez pas à gérer de grandes opérations de copie.

+0

En quoi cette approche est-elle différente de ce que Nick Haddad a suggéré en utilisant Boost Pointer Container Library? –

+8

@ ThorstenSchöning std :: shared_ptr n'ajoute pas de dépendance à boost. –

11

Généralement, le stockage des objets directement dans le conteneur STL est le plus simple car il est le plus simple, le plus efficace et le plus simple pour utiliser l'objet.

Si votre objet lui-même a une syntaxe non copiable ou est un type de base abstraite, vous devez stocker des pointeurs (est plus facile à utiliser shared_ptr)

+3

Ce n'est pas plus efficace si vos objets sont volumineux et que vous déplacez des éléments fréquemment. – allyourcode

44

Cela dépend vraiment de votre situation. Si vos objets sont petits et que la copie de l'objet est légère, le stockage des données dans un conteneur stl est simple et facile à gérer car vous n'avez pas à vous soucier de la gestion de la durée de vie.

Si vos objets sont volumineux et que le constructeur par défaut n'a pas de sens, ou si les copies d'objets sont chères, le stockage avec des pointeurs est probablement la solution.

Si vous décidez d'utiliser des pointeurs sur des objets, jetez un œil au Boost Pointer Container Library. Cette bibliothèque d'amplification enveloppe tous les conteneurs STL à utiliser avec les objets alloués dynamiquement. Chaque conteneur de pointeur (par exemple ptr_vector) prend possession d'un objet lorsqu'il est ajouté au conteneur et gère la durée de vie de ces objets pour vous. Vous accédez également à tous les éléments d'un conteneur ptr_ par référence. Cela vous permet de faire des choses comme

class BigExpensive { ... } 

// create a pointer vector 
ptr_vector<BigExpensive> bigVector; 
bigVector.push_back(new BigExpensive("Lexus", 57700)); 
bigVector.push_back(new BigExpensive("House", 15000000); 

// get a reference to the first element 
MyClass& expensiveItem = bigList[0]; 
expensiveItem.sell(); 

Ces classes enveloppent les conteneurs STL et fonctionnent avec tous les algorithmes de STL, ce qui est très pratique.

Il existe également des fonctions permettant de transférer la propriété d'un pointeur dans le conteneur à l'appelant (via la fonction de libération dans la plupart des conteneurs).

2

Si les objets doivent être référencés ailleurs dans le code, stockez-les dans un vecteur de boost :: shared_ptr. Cela garantit que les pointeurs vers l'objet resteront valides si vous redimensionnez le vecteur.

Ie:

std::vector<boost::shared_ptr<protocol> > protocols; 
... 
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized 

Si stocke des pointeurs Noone else aux objets, ou la liste ne se développe pas et rétrécir, magasin juste des objets comme vieux de plaine:

std::vector<protocol> protocols; 
connection c(protocols[0]); // value-semantics, takes a copy of the protocol 
21

Désolé sauter 3 ans après l'événement, mais une mise en garde ici ...

Lors de mon dernier gros projet, ma structure de données centrale était un ensemble d'objets assez simples. Environ un an après le début du projet, au fur et à mesure que les exigences évoluaient, j'ai réalisé que l'objet devait être polymorphe. Il a fallu quelques semaines de chirurgie cérébrale difficile et désagréable pour que la structure de données soit un ensemble de pointeurs de classe de base, et pour gérer tous les dommages collatéraux dans le stockage d'objets, le casting, etc. Il m'a fallu quelques mois pour me convaincre que le nouveau code fonctionnait. Incidemment, cela m'a fait réfléchir sur la façon dont le modèle d'objet C++ est bien conçu.

Sur mon gros projet actuel, ma structure de données centrale est un ensemble d'objets assez simples. Environ un an après le début du projet (ce qui arrive aujourd'hui), j'ai réalisé que l'objet devait être polymorphe. Retour au net, trouvé ce fil, et trouvé le lien de Nick à la bibliothèque du conteneur pointeur Boost. C'est exactement ce que j'ai dû écrire la dernière fois pour tout réparer, alors je vais essayer cette fois-ci.

La morale, pour moi, de toute façon: si votre spécification n'est pas 100% coulée dans la pierre, optez pour des pointeurs, et vous pouvez potentiellement vous épargner beaucoup de travail plus tard.

+0

Les spécifications ne sont jamais gravées dans la pierre. Je ne pense pas que cela signifie que vous devriez utiliser des conteneurs de pointeurs d'utilisation exclusivement, bien que les conteneurs de pointeurs Boost semblent rendre cette option beaucoup plus attrayante. Je suis sceptique que vous devez réviser votre programme entier tout à la fois si vous décidez qu'un conteneur d'objet doit être converti en un conteneur de pointeur. Cela pourrait être le cas sous certaines conceptions. Dans ce cas, c'est un design fragile. Dans ce cas, ne blâmez pas votre problème sur la "faiblesse" des conteneurs d'objets. – allyourcode

+0

Vous auriez pu laisser l'élément dans le vecteur avec la sémantique des valeurs et faire le comportement polymorphe à l'intérieur. –

1

Cette question m'a embêté pendant un certain temps.Je me penche sur les pointeurs de stockage, mais j'ai quelques exigences supplémentaires (SWIG lua wrappers) qui pourraient ne pas s'appliquer à vous.

Le point le plus important dans ce post est de test, il vous, en utilisant vos objets

Je l'ai fait aujourd'hui pour tester la vitesse d'appeler une fonction membre d'une collection de 10 millions d'objets, 500 fois.

La fonction met à jour x et y en fonction de xdir et ydir (toutes les variables membres flottantes).

J'ai utilisé une liste std :: list pour contenir les deux types d'objets, et j'ai trouvé que le stockage de l'objet dans la liste est légèrement plus rapide que l'utilisation d'un pointeur. D'autre part, la performance était très proche, donc il s'agit de savoir comment ils seront utilisés dans votre application. Pour référence, avec -O3 sur mon matériel les pointeurs ont pris 41 secondes pour terminer et les objets bruts ont pris 30 secondes pour terminer.

Questions connexes