5 votes

Array : Stockage d'objets ou de références

En tant que développeur Java, j'ai la question C++ suivante.

Si j'ai des objets de type A et que je veux en stocker une collection dans un tableau, dois-je simplement stocker des pointeurs vers les objets ou est-il préférable de stocker l'objet lui-même ?

À mon avis, il est préférable de stocker des pointeurs pour les raisons suivantes 1) on peut facilement supprimer un objet en mettant son pointeur à null 2) On économise de l'espace.

7voto

Flexo Points 39273

Pointeurs ou seulement les objets ?

En C++, il n'est pas possible de mettre des références dans un tableau. On peut faire un tableau de pointeurs, mais je préférerais toujours un conteneur et des objets réels plutôt que des pointeurs parce que.. :

  1. Il n'y a pas de risque de fuite, la sécurité exceptionnelle est plus facile à gérer.
  2. Ce n'est pas moins d'espace - si vous stockez un tableau de pointeurs, vous avez besoin de la mémoire pour l'objet plus la mémoire pour un pointeur.

Les seules fois où je préconiserais de mettre des pointeurs (ou des pointeurs intelligents, ce serait mieux) dans un conteneur (ou un tableau si vous devez le faire), c'est lorsque votre objet n'est pas constructible et assignable par copie (une exigence pour les conteneurs, les pointeurs répondent toujours à cette exigence) ou lorsque vous avez besoin qu'ils soient polymorphes. Par exemple

#include <vector>

struct foo {
  virtual void it() {}
};

struct bar : public foo {
  int a;
  virtual void it() {}
}; 

int main() {
  std::vector<foo> v;
  v.push_back(bar()); // not doing what you expected! (the temporary bar gets "made into" a foo before storing as a foo and your vector doesn't get a bar added)
  std::vector<foo*> v2;
  v2.push_back(new bar()); // Fine
}

Si vous voulez suivre cette voie conteneurs de pointeurs d'appoint peuvent être intéressantes car elles font tout le travail à votre place.

Retrait de tableaux ou de conteneurs.

Affectation NULL ne réduit pas le nombre de pointeurs dans votre conteneur/ tableau, (il ne gère pas la fonction delete soit), la taille reste la même mais il y a maintenant des pointeurs que vous ne pouvez pas déréférencer légalement. Cela rend le reste de votre code plus complexe sous la forme d'instructions if supplémentaires et interdit des choses comme :

// need to go out of our way to make sure there's no NULL here
std::for_each(v2.begin(),v2.end(), std::mem_fun(&foo::it));

Je n'aime pas du tout l'idée d'autoriser NULL dans des séquences de pointeurs en général, car vous finissez rapidement par enterrer tout le travail réel dans une séquence d'instructions conditionnelles. L'alternative est la suivante std::vector fournit une erase qui prend un itérateur afin que vous puissiez écrire :

v2.erase(v2.begin());

pour supprimer le premier ou le v2.begin()+1 pour le second. Il n'y a pas de méthode simple pour "effacer le nième élément" sur std::vector en raison de la complexité temporelle - si vous effectuez beaucoup d'effacements, il existe d'autres conteneurs qui pourraient être plus appropriés.

Pour un tableau, vous pouvez simuler l'effacement avec :

#include <utility>
#include <iterator>
#include <algorithm>
#include <iostream>

int main() {
  int arr[] = {1,2,3,4};
  int len = sizeof(arr)/sizeof(*arr);
  std::copy(arr, arr+len, std::ostream_iterator<int>(std::cout, " "));
  std::cout << std::endl;

  // remove 2nd element, without preserving order:
  std::swap(arr[1], arr[len-1]);
  len -= 1;
  std::copy(arr, arr+len, std::ostream_iterator<int>(std::cout, " "));
  std::cout << std::endl;

  // and again, first element:
  std::swap(arr[0], arr[len-1]);
  len -= 1;
  std::copy(arr, arr+len, std::ostream_iterator<int>(std::cout, " "));
  std::cout << std::endl;
}

Le maintien de l'ordre nécessite une série de mélanges au lieu d'un seul échange, ce qui illustre bien la complexité de l'effacement des données. std::vector les visages. Bien sûr, en faisant cela, vous venez de réinventer une roue assez grande, avec beaucoup moins d'utilité et de souplesse qu'un conteneur de bibliothèque standard ne le ferait pour vous gratuitement !

1voto

JaredPar Points 333733

Il semble que vous confondiez les références et les pointeurs. Le C++ a trois façons courantes de représenter les poignées d'objets

  • Références
  • Pointeurs
  • Valeurs

En provenance de Java, la méthode la plus analogue consiste à utiliser un pointeur. C'est probablement ce que vous essayez de faire ici.

La façon dont ils sont stockés a cependant des effets assez fondamentaux sur leur comportement. Lorsque vous les stockez sous forme de valeur, vous avez souvent affaire à des copies des valeurs. Les pointeurs, quant à eux, traitent un objet avec plusieurs références. Il n'est pas possible de répondre de manière catégorique à la question de savoir si l'un est meilleur que l'autre sans disposer d'un peu plus de contexte sur ce que font ces objets.

1voto

w00te Points 11664

Cela dépend complètement de ce que vous voulez faire... mais vous êtes mal conseillé à certains égards.

Voici ce qu'il faut savoir :

  1. En C++, il n'est pas possible de définir une référence à NULL, mais on peut définir un pointeur à NULL.
  2. Une référence ne peut être faite qu'à un objet existant - elle doit être initialisée en tant que telle.
  3. Une référence ne peut pas être modifiée (bien que la valeur référencée puisse l'être).
  4. Vous ne gagneriez pas d'espace, en fait vous en utiliseriez plus puisque vous utilisez un objet et une référence. Si vous devez référencer le même objet plusieurs fois, vous économisez de l'espace, mais vous pourriez tout aussi bien utiliser un pointeur - c'est plus flexible dans la PLUPART (lire : pas tous) des scénarios.
  5. Un dernier point important : Les conteneurs STL (vecteur, liste, etc.) ont la sémantique COPY - ils ne peuvent pas travailler avec des références. Ils peuvent fonctionner avec des pointeurs, mais cela devient compliqué, donc pour l'instant vous devriez toujours utiliser des objets copiables dans ces conteneurs et accepter qu'ils soient copiés, que vous le vouliez ou non. La STL est conçue pour être efficace et sûre avec la sémantique de copie.

J'espère que cela vous aidera ! :)

PS (EDIT) : Vous pouvez utiliser certaines nouvelles fonctionnalités de BOOST/TR1 (recherchez-les sur Google), et créer un conteneur/réseau de shared_ptr (pointeurs intelligents à comptage de références) qui vous donnera une sensation similaire aux références et au garbage collection de Java. Il y a une multitude de différences, mais vous devrez les lire vous-même - ce sont des caractéristiques importantes de la nouvelle norme.

1voto

Mike Seymour Points 130519

Vous devez toujours stocker les objets lorsque c'est possible ; ainsi, le conteneur gérera la durée de vie des objets pour vous.

De temps en temps, vous devrez stocker des pointeurs ; le plus souvent, des pointeurs vers une classe de base où les objets eux-mêmes seront de types différents. Dans ce cas, vous devez veiller à gérer vous-même la durée de vie des objets, en vous assurant qu'ils ne sont pas détruits lorsqu'ils se trouvent dans le conteneur, mais qu'ils sont détruits une fois qu'ils ne sont plus nécessaires.

Contrairement à Java, l'attribution d'une valeur nulle à un pointeur n'a pas d'effet sur la qualité de l'information. pas désallouer l'objet pointé ; au lieu de cela, vous obtenez une fuite de mémoire s'il n'y a plus de pointeurs vers l'objet. Si l'objet a été créé à l'aide de new alors delete doit être appelé à un moment donné. La meilleure solution consiste à stocker des pointeurs intelligents ( shared_ptr ou peut-être unique_ptr si elle est disponible), ou d'utiliser les conteneurs de pointeurs de Boost.

0voto

NPE Points 169956

Il n'est pas possible de stocker des références dans un conteneur. Vous pourriez stocker des pointeurs (nus) à la place, mais cela est sujet à des erreurs et est donc mal vu.

Le véritable choix se situe donc entre le stockage d'objets et les pointeurs intelligents vers des objets. Les deux ont leur utilité. Je recommanderais d'opter pour le stockage des objets par valeur, à moins que la situation particulière n'exige le contraire. Cela pourrait arriver :

  • si vous avez besoin de NULLER l'objet sans le retirer de la base de données conteneur ;
  • si vous avez besoin de stocker des pointeurs sur le même objet dans des plusieurs conteneurs ;
  • si vous devez traiter les éléments du conteneur de manière polymorphe.

Une raison de pas le font pour économiser de l'espace, puisque le stockage des éléments par valeur est susceptible d'être plus efficace en termes d'espace.

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