Quelle serait la meilleure façon de stocker des objets de classes dérivées d'une interface commune dans un conteneur commun?

StackOverflow https://stackoverflow.com/questions/2489533

Question

Je voudrais gérer un tas d'objets de classes dérivées d'une classe d'interface partagée dans un conteneur commun.

Pour illustrer le problème, disons que je construis un jeu qui contiendra différents acteurs. Appelons le IActor d'interface et d'en tirer Enemy et Civilian de celui-ci.

Maintenant, l'idée est d'avoir mon jeu de boucle principale pouvoir faire ceci:

// somewhere during init
std::vector<IActor> ActorList;
Enemy EvilGuy; 
Civilian CoolGuy;
ActorList.push_back(EvilGuy);
ActorList.push_back(CoolGuy);

et

// main loop
while(!done) {
    BOOST_FOREACH(IActor CurrentActor, ActorList) {
        CurrentActor.Update();
        CurrentActor.Draw();
    }
}

... ou quelque chose le long de ces lignes. Cet exemple évidemment ne fonctionnera pas, mais qui est à peu près la raison pour laquelle je demande ici.

Je voudrais savoir: Quelle serait la meilleure, la plus sûre, plus haut niveau moyen de gérer ces objets dans un conteneur hétérogène commun? Je sais que sur une variété d'approches (Boost :: Tous, void *, classe de gestionnaire avec boost :: shared_ptr, Container Boost.Pointer, dynamic_cast) mais je ne peux pas décider qui serait la voie à suivre ici.

De plus, je voudrais souligner que je veux rester aussi loin que possible de la gestion de la mémoire manuelle ou pointeurs imbriqués.

Aide appréciée.)

Était-ce utile?

La solution

Comme vous l'avez deviné, vous devez stocker les objets comme des pointeurs.
Je préfère utiliser les conteneurs pointeur de la poussée (plutôt qu'un conteneur normal des pointeurs intelligents).

La raison est l'accès au conteneur ptr boost les objets comme si elles étaient des objets (références retour) plutôt que des pointeurs. Cela rend plus facile à utiliser foncteurs et des algorithmes standards sur les conteneurs.

L'inconvénient des pointeurs intelligents est que vous partagez la propriété.
Ce n'est pas ce que vous voulez vraiment. Vous voulez la propriété d'être dans un seul endroit (dans ce cas, le conteneur).

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian());

et

std::for_each(ActorList.begin(), 
              ActorList.end(),
              std::mem_fun_ref(&IActor::updateDraw));

Autres conseils

Pour résoudre le problème que vous avez mentionné, bien que vous allez dans la bonne direction, mais vous le faites dans le mauvais sens. Voici ce que vous devez faire

  • Définir une classe de base (que vous faites déjà) avec des fonctions virtuelles qui seraient surchargées par des classes dérivées Enemy et Civilian dans votre cas.
  • Vous devez choisir un récipient approprié avec stockera votre objet. Vous avez pris un std::vector<IActor> qui n'est pas un bon choix car
    • Tout d'abord lorsque vous ajoutez des objets au vecteur, il est conduit à trancher objet. Cela signifie que seule la partie de IActor de Enemy ou Civilian est stocké au lieu de l'objet entier.
    • Deuxièmement, vous devez appeler des fonctions en fonction du type de l'objet (virtual functions), qui ne peut se produire si vous utilisez des pointeurs.

Les deux raisons ci-dessus soulignent le fait que vous devez utiliser un récipient qui peut contenir des pointeurs, quelque chose comme std::vector<IActor*>. Mais un meilleur choix serait d'utiliser container of smart pointers qui vous permettra d'économiser de maux de tête de gestion de la mémoire. Vous pouvez utiliser l'un des pointeurs intelligents en fonction de vos besoins (mais pas auto_ptr)

est ce que votre code ressemblerait

// somewhere during init
std::vector<some_smart_ptr<IActor> > ActorList;
ActorList.push_back(some_smart_ptr(new Enemy()));
ActorList.push_back(some_smart_ptr(new Civilian()));

et

// main loop
while(!done) 
{
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    {
        CurrentActor->Update();
        CurrentActor->Draw();
     }
}

Ce qui est à peu près semblable à votre code d'origine, sauf pour une partie intelligente des pointeurs

Ma réaction immédiate est que vous devez stocker des pointeurs intelligents dans le conteneur, et assurez-vous que la classe de base définit assez (pur) des méthodes virtuelles que vous ne devez dynamic_cast à la classe dérivée.

Si vous voulez que le conteneur à posséder exclusivement les éléments, utilisez un conteneur de pointeur Boost: ils sont conçus pour ce travail. Sinon, utilisez un récipient de shared_ptr<IActor> (et bien sûr les utiliser correctement, ce qui signifie que tout le monde qui a besoin de partager la propriété utilise shared_ptr).

Dans les deux cas, assurez-vous que le destructor de IActor est virtuelle.

void* vous oblige à faire la gestion de la mémoire manuelle, de sorte que est sorti. Boost.Any est surpuissant quand les types sont liés par héritage -. Polymorphisme norme fait le travail

Que vous ayez besoin dynamic_cast ou non est une question orthogonale - si les utilisateurs du conteneur seulement besoin de l'interface IActor, et vous (a) faire toutes les fonctions de l'interface virtuelle, ou bien (b) utiliser la non idiome d'interface virtuelle, vous n'avez pas besoin dynamic_cast. Si les utilisateurs du conteneur savent que certains des objets IActor sont « vraiment » les civils, et que vous voulez utiliser les choses qui sont dans l'interface civile mais pas IActor, alors vous aurez besoin des moulages (ou une refonte).

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