Concevoir un nouveau système à partir de zéro. J'utiliserai la STL pour stocker des listes et des cartes de certains objets à longue durée de vie.
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 vie et la portée et de simplement stocker les pointeurs vers ces objets dans mes conteneurs STL?
Je me rends compte que les détails sont un peu brefs, mais je cherche la meilleure réponse "théorique" si elle existe, car je sais que ces deux solutions sont possibles.
Deux inconvénients très évidents à jouer avec les pointeurs: 1) Je dois gérer l’affectation/désallocation de ces objets moi-même dans une étendue au-delà du TSL. 2) Je ne peux pas créer d'objet temporaire sur la pile et l'ajouter à mes conteneurs.
Y a-t-il autre chose qui me manque?
Depuis que les gens ressentent l'efficacité d'utiliser des pointeurs.
Si vous envisagez d'utiliser un vecteur std :: et que les mises à jour sont rares et que vous effectuez des itérations sur votre collection, il s'agit d'un type de stockage non polymorphe. Les "copies" des objets seront plus efficaces car vous obtiendrez une meilleure localisation.
Otoh, si les mises à jour sont communes, le stockage des pointeurs économisera les coûts de copie/réinstallation.
Cela dépend vraiment de votre situation.
Si vos objets sont petits et que leur copie est légère, le stockage des données dans un conteneur stl est simple et facile à gérer à mon avis, car vous n'avez pas à vous soucier de la gestion de la durée de vie.
Si vos objets sont volumineux et qu’un constructeur par défaut n’a pas de sens, ou que les copies d’objets sont coûteuses, alors stocker avec des pointeurs est probablement la solution.
Si vous décidez d'utiliser des pointeurs sur des objets, jetez un coup d'œil à Boost Pointer Container Library . Cette bibliothèque boost englobe tous les conteneurs STL à utiliser avec des objets alloués dynamiquement.
Chaque conteneur de pointeur (par exemple, ptr_vector) devient propriétaire 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 encapsulent les conteneurs STL et fonctionnent avec tous les algorithmes STL, ce qui est très pratique.
Il existe également des fonctions permettant de transférer à l'appelant la propriété d'un pointeur situé dans le conteneur (via la fonction de libération dans la plupart des conteneurs).
Si vous stockez des objets polymorphes, vous devez toujours utiliser une collection de pointeurs de classe de base.
C’est-à-dire que si vous envisagez de stocker différents types dérivés dans votre collection, vous devez stocker les pointeurs ou être mangé par le démon de découpage.
Désolé de sauter dans 3 ans après l'événement, mais une mise en garde ici ...
Sur mon dernier grand projet, ma structure de données centrale était un ensemble d’objets assez simples. Environ un an après le début du projet, à mesure que les exigences évoluaient, je me suis rendu compte que l’objet devait en réalité ê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 liés au stockage d’objets, au casting, etc. Il m'a fallu quelques mois pour me convaincre que le nouveau code fonctionnait. Incidemment, cela m'a fait réfléchir à la façon dont le modèle objet C++ est bien conçu.
Sur mon grand 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 est le cas aujourd'hui), j'ai réalisé que l'objet devait en fait être polymorphe. Retour sur le net, a trouvé ce fil et trouvé le lien de Nick vers la bibliothèque de conteneurs du pointeur Boost. C’est exactement ce que j’avais dû écrire la dernière fois pour tout régler, je vais donc tenter le coup cette fois-ci.
La morale, pour moi, en tout cas: si vos spécifications ne sont pas immuables dans le marbre, cherchez des indicateurs et vous pourriez potentiellement vous épargner beaucoup de travail plus tard.
Pourquoi ne pas tirer le meilleur parti des deux mondes: créez un conteneur de pointeurs intelligents (tels que boost::shared_ptr
ou std::shared_ptr
). Vous n'avez pas à gérer la mémoire, et vous n'avez pas à gérer d'opérations de copie volumineuses.
Généralement, stocker les objets directement dans le conteneur STL est préférable car il est le plus simple, le plus efficace et le plus facile à utiliser.
Si votre objet a lui-même une syntaxe non-copiable ou est un type de base abstrait, vous devrez stocker les pointeurs (le plus simple consiste à utiliser shared_ptr)
Vous semblez bien comprendre la différence. Si les objets sont petits et faciles à copier, stockez-les bien.
Sinon, je penserais à stocker les pointeurs intelligents (et non auto_ptr, un pointeur intelligent de comptage des références) sur ceux que vous allouez sur le tas. De toute évidence, si vous optez pour des pointeurs intelligents, vous ne pouvez pas stocker les objets alloués à la pile temporaire (comme vous l'avez dit).
@ Torbjörn insiste sur le slicing.
L'utilisation de pointeurs sera plus efficace, car les conteneurs ne copieront que des pointeurs au lieu d'objets pleins.
Vous trouverez ici des informations utiles sur les conteneurs STL et les pointeurs intelligents:
Pourquoi est-il erroné d'utiliser std :: auto_ptr <> avec des conteneurs standard?
Si les objets doivent être référencés ailleurs dans le code, enregistrez dans un vecteur boost :: shared_ptr. Cela garantit que les pointeurs sur l'objet resteront valides si vous redimensionnez le vecteur.
C'est à dire:
std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized
Si personne ne stocke de pointeurs sur les objets, ou si la liste ne s'agrandit pas et ne se réduit pas, stockez-la simplement en tant qu'objets anciens:
std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol
Cette question me dérange depuis un moment.
Je me penche pour stocker des pointeurs, mais j'ai des exigences supplémentaires (enveloppes SWIG lua) qui pourraient ne pas s'appliquer à vous.
Le point le plus important dans ce post est de testez vous-même, en utilisant vos objets
Je l’ai fait aujourd’hui pour tester la vitesse d’appel d’une fonction membre sur une collection de 10 millions d’objets, 500 fois.
La fonction met à jour x et y en fonction de xdir et de ydir (toutes les variables de membre float).
J'ai utilisé un std :: list pour contenir les deux types d'objets et j'ai constaté que le stockage de l'objet dans la liste est légèrement plus rapide que l'utilisation d'un pointeur. D'autre part, les performances étaient très proches, il en va donc de la manière dont elles seront utilisées dans votre application.
Pour référence, avec -O3 sur mon matériel, les pointeurs prenaient 41 secondes et les objets bruts, 30 secondes.