Comment écrire du code polymorphique en C++?

je suis en train d'écrire un morceau de code avec des exigences élevées sur la performance où je dois gérer un grand nombre d'objets d'une manière polymorphe. Disons que j'ai une classe A et une classe B qui est dérivée de A. je pouvais maintenant créer un vecteur de B:s comme ceci

vector<A*> a(n);
for(int i = 0; i < n; i++)
  a[i] = new B();

mais si n est grand (dans l'ordre de 10^6 ou plus dans mon cas) cela nécessiterait de très nombreux appels à de nouveaux objets et de plus les n pourraient potentiellement être répartis sur toute ma mémoire principale résultant en une très mauvaise cache performance. Quelle serait la bonne façon de faire face à cette situation? Je pense à faire quelque chose comme ce qui suit pour avoir tous les objets dans une région de mémoire contiguë.

B* b = new B[n];
vector<A*> a(n);
for(int i = 0; i < n; i++)
  a[i] = b + i;

mais un problème est de savoir comment libérer la mémoire allouée par new B[n] si b n'est plus disponible (mais nous avons encore un). J'ai juste appris que le fait d'essayer

delete[] a[0];

n'est pas une bonne idée...

10
demandé sur Martin 2011-03-10 17:27:45

4 réponses

Vous pouvez utiliser le placement nouveau pour construire un objet à un emplacement mémoire particulier:

vector<A*> a(n);
for(int i = 0; i < n; i++)
  a[i] = new(storage + i*object_size) B();
  // and invoke the destructor manually to release the object (assuming A has a virtual destructor!)
  a[i]->~A(); 

mais vous ne pouvez pas résoudre le "vrai" problème sans renoncer au stockage continu: si un objet est libéré, il causera un trou dans le tas, provoquant ainsi une forte fragmentation au fil du temps. Vous ne pouviez garder trace des objets libérés et réutiliser le stockage.

2
répondu Alexander Gessler 2011-03-10 14:35:58

si vous êtes sûr que ce ne seront que des objets de type B pourquoi ne pas utiliser un vecteur parallèle:

vector<B> storage(n);
vector<A*> pointers(n);
for(int i = 0; i < n; i++)
   pointers[i] = &storage[i];
7
répondu sharptooth 2011-03-10 14:33:38

si vous voulez garder tous vos objets dans une mémoire contiguë et, en même temps, éviter d'utiliser une indication indirecte (un vecteur de pointeurs de classe de base), vous pouvez utiliser un conteneur de type union, par exemple un vecteur boost::variant. Cela suppose, bien entendu, qu'il existe un nombre limité et connu de classes dérivées et que leur taille est comparable. L'inconvénient est que chaque élément du vecteur prendra autant de mémoire que la plus grande classe dérivée, indépendamment de leur classe réelle (et il suppose également vos cours sont pas chers à copier). Les avantages sont que vous avez le stockage hétérogène contigu des objets polymorphiques, type-sécurité, et aucune indirecte lors de l'accès aux éléments. Voici un exemple de base avec boost::variant et trois classes A,B,C, où les deux B et C hérite de A, et ils sont tous polymorphes (et, bien sûr, cela pourrait être beaucoup plus agréable avec un peu de sucre-revêtement et/ou quelque chose de plus spécialisé pour votre but, pas boost::variant ce qui n'est pas vraiment approprié à cette fin):

#include <iostream>
#include <vector>
#include <boost/variant/variant.hpp>
#include <boost/variant/apply_visitor.hpp>

struct A {
  int v1;
  A(int aV1 = 0) : v1(aV1) { };
  virtual ~A() { };
  virtual void print() { std::cout << "A print " << v1 << std::endl; };
  struct get_ref {
    typedef A& result_type;
    template <class T>
    A& operator()(T& obj) const { return obj; };
  };
};

struct B : A {
  float f1;
  B(float aF1 = 0.0) : A(42), f1(aF1) { };
  ~B() { };
  virtual void print() { std::cout << "B print " << f1 << std::endl; };
};

struct C : A {
  double d1;
  C(double aD1 = 0.0) : A(42), d1(aD1) { };
  ~C() { };
  virtual void print() { std::cout << "C print " << d1 << std::endl; };
};

int main() {
  typedef boost::variant<A,B,C> value_type;
  typedef std::vector< value_type > vect_type;
  vect_type arr(15);
  int i=0;
  for(;i<5;++i) arr[i] = A(31);
  for(;i<10;++i) arr[i] = B(0.2);
  for(;i<15;++i) arr[i] = C(0.4);

  for(vect_type::iterator it = arr.begin(); it != arr.end(); ++it)
    boost::apply_visitor(A::get_ref(), *it).print();

  std::cout << "value_type has size of " << sizeof(value_type) << std::endl;
  std::cout << "compared to: sizeof(A)=" << sizeof(A) << " sizeof(B)=" << sizeof(B) << " sizeof(C)=" << sizeof(C) << std::endl;

  return 0;
};
1
répondu Mikael Persson 2011-03-10 17:57:17

vous avez juste à stocker les pointeurs retournés de new[] quelque part, et les supprimer[] plus tard. D'un autre vecteur, par exemple.

0
répondu Bo Persson 2011-03-10 16:58:15