Question

Prenons l'exemple de code suivant:

class Foo
{
};

class Bar : public Foo
{
};

class FooCollection
{
protected:
    vector<shared_ptr<Foo> > d_foos;
};

class BarCollection : public FooCollection
{
public:
    vector<shared_ptr<Bar> > &getBars()
    {
        // return d_foos won't do here...
    }
};

J'ai un problème comme celui-ci dans mon projet en cours. Le code client utilise BarCollection, qui stocke des pointeurs vers Bars dans d_foos qui est déclaré dans FooCollection. Je voudrais maintenant exposer la collection de pointeurs vers des barres au code client. Je pouvais donner le code client l'accès au vecteur de pointeurs vers Foos et jeter ceux-ci à des pointeurs vers Bars dans le code client, mais cela ne semble pas correct puisque le client n'a pas à connaître l'existence de Foo.

Je pourrais aussi définir un élément de get() qui récupère les objets de d_foos et les jette, mais cela se sent tout à fait maladroite. De préférence, je voudrais revenir juste d_foos comme vector<shared_ptr<Bar> > &, mais je ne peux pas sembler le faire.

Il se pourrait aussi que ma conception est tout simplement faux. Il semblait la solution la plus naturelle que, comme Bar est une spécialisation de Foo et BarCollection est une spécialisation de FooCollection et ils partagent la fonctionnalité.

Pouvez-vous suggérer des solutions agréables pour mettre en œuvre getBars dans BarCollection ou de meilleures alternatives de conception?

Modifier

Il s'avère que ma conception était mauvais effet. BarCollection n'est pas un FooCollection, en dépit d'exiger toutes les fonctionnalités de FooCollection. Ma solution actuelle basée sur les réponses ci-dessous - ce qui est beaucoup plus propre - est maintenant:

class Foo
{
};

class Bar : public Foo
{
};

template<class T>
class Collection
{
    vector<shared_ptr<T> > d_items;
};

typedef Collection<Foo> FooCollection;

class BarCollection : public Collection<Bar>
{
    // Additional stuff here.
};

Merci pour toutes les excellentes suggestions et des exemples!

Était-ce utile?

La solution

template<class T>
class MyContainer {
  vector<shared_ptr<T> > d_foos;
public:
  vector<shared_ptr<T> > & getVector();
};

class FooCollection : public MyContainer<Foo> {
};

class BarCollection : public MyContainer<Bar> {
};

Autres conseils

Je suggère d'exposer itérateurs de vos classes de conteneurs, au lieu du conteneur membre. De cette façon, il ne sera pas question ce que le type de conteneur est.

Le problème est que vous essayez de mélanger et match deux différents types joli beaucoup indépendants de polymorphisme d'une manière qui ne fonctionnera pas. La compilation, le polymorphisme de type sécurisé de modèles ne vous permettra pas de substituer un type de base pour un type dérivé. système de modèle de C ++ ne fait aucune association entre

class<Foo>

et

class<Bar>

Une suggestion pourrait être de créer un adaptateur dérivé de Foo qui renversera la classe correcte:

 template <class derived, class base>
 class DowncastContainerAdapter
 {
 private:
     std::vector< boost::shared_ptr<base> >::iterator curr;
     std::vector< boost::shared_ptr<base> >::const_iterator end;
 public:
     DowncastContainerAdapter(/*setup curr & end iterators*/)
     {
         // assert derived actually is derived from base
     }

     boost::shared_ptr<derived> GetNext()
     {
         // increment iterator
         ++curr;
         return dynamic_cast<base>(*curr);
     }

     bool IsEnd()
     {
         return (curr == end);
     }
 };

Notez cette classe aura le même problème que itérateur, opération sur le vecteur peut invalider cette classe.

Une autre pensée

Vous ne pouvez pas le réaliser, mais il peut être parfaitement bien de revenir juste un vecteur de Foo. L'utilisateur du bar doit déjà avoir une connaissance complète de Foo, car en incluant Bar.h ils doivent obtenir .h par Bar.h. La raison étant que pour Bar à hériter de Foo, il doit avoir une connaissance complète de la classe via Foo.h. Je suggère plutôt que d'utiliser la solution ci-dessus, s'il est possible faire Foo (ou un super classe de Foo) une classe d'interface et passer autour des vecteurs de pointeurs vers cette classe d'interface. Ceci est un modèle vu assez souvent et ne soulèvera pas les sourcils que cette solution bancale je suis venu avec peut :). Là encore, vous pouvez avoir vos raisons. Bonne chance dans les deux cas.

La question est, pourquoi voudriez-vous faire? Si vous donnez à l'utilisateur une collection de pointeurs à la barre, vous assumez, qu'il n'y a que Bars, donc le stockage interne des pointeurs dans une collection Foo n'a pas de sens. Si vous stockez différents sous-types de Foo dans votre collection de pointeur vers Foo, vous ne pouvez pas revenir comme une collection de pointeurs vers Bar, car tous les objets il y a des bars. Dans le premier cas, (vous savez que vous avez seulement des bars), vous devez utiliser une approche modélisée comme suggéré ci-dessus. Dans le cas contraire, il faut repenser, ce que vous voulez vraiment.

Pouvez-vous remplacer pas avec une collection de chaque Foo sans canevas / Bar ?, quelque chose comme ceci

class Collection<T> {
protected:
    vector<shared_ptr<T> > d_foos;
};

typedef Collection<Foo> FooCollection;
typedef Collection<Bar> BarCollection;

Avez-vous un besoin particulier d'avoir BarCollection dérivé de FooCollection? Parce que généralement un BarCollection est pas FooCollection, généralement beaucoup de choses qui peut être fait avec un FooCollection ne doit pas être fait avec un BarCollection. Par exemple:

BarCollection *bc = new BarCollection();
FooCollection *fc = bc; // They are derived from each other to be able to do this
fc->addFoo(Foo());      // Of course we can add a Foo to a FooCollection

Maintenant, nous avons ajouté un objet Foo à ce qui est censé être un BarCollection. Si le BarCollection tente d'accéder à cet élément nouvellement ajouté et attend que ce soit un Bar, toutes sortes de vilaines choses vont se produire.

Donc, en général vous voulez éviter cela et ne pas vos classes de collection dérivées de l'autre. Voir aussi à propos de

Tout d'abord, parlons shared_ptr. Connaissez-vous: boost::detail::dynamic_cast_tag

shared_ptr<Foo> fooPtr(new Bar());
shared_ptr<Bar> barPtr(fooPtr, boost::detail::dynamic_cast_tag());

Ceci est une façon très pratique. Sous le couvercle, il effectue juste un dynamic_cast, rien là de fantaisie, mais une notation plus facile. Le contrat est le même que le classique: si l'objet pointé n'est pas réellement un Bar (ou dérivé de celui-ci), vous obtenez un pointeur NULL

.

Retour à votre question. CODE BAD

BarCollection n'est pas un FooCollection, comme mentionné, vous êtes donc en difficulté parce que vous pouvez introduire d'autres éléments dans le vecteur des pointeurs qui Bar les.

Je ne prolongerons pas sur ce bien, parce que cela est au-delà de la question à portée de main, et je pense que nous (comme ceux qui tentent de répondre) nous devons retenir de cela.

Vous ne pouvez pas passer une référence, mais vous pouvez passer un View.

En fait, un View est un nouvel objet qui agissent comme Proxy à l'ancienne. Il est relativement facile en utilisant Boost.Iterators de par exemple.

class VectorView
{
  typedef std::vector< std::shared_ptr<Foo> > base_type;

public:
  typedef Bar value_type;
  // all the cluttering

  class iterator: boost::iterator::iterator_adaptor<
    iterator,
    typename base_type::iterator,
    std::shared_ptr<Bar>
  >
  {
    typename iterator_adaptor::reference dereference() const
    {
      // If you have a heart weakness, you'd better stop here...
      return reinterpret_cast< std::shared_ptr<Bar> >(this->base_reference());
    }
  };

  // idem for const_iterator

  // On to the method forwarding
  iterator begin() { return iterator(m_reference.begin()); }

private:
  base_type& m_reference;
}; // class VectorView

Le vrai problème est ici bien sûr le bit reference. Obtenir un objet NEW de shared_ptr est facile et permet d'effectuer une dynamic_cast au besoin. Obtenir un reference au ORIGINAL de shared_ptr mais interprété comme le type requis ... est vraiment pas ce que je veux voir dans le code.

Remarque :
Il pourrait y avoir un moyen de faire mieux que cela en utilisant Boost.Fusion transform_view classe, mais je ne pouvais pas comprendre.

En particulier, en utilisant transform_view je peux obtenir shared_ptr<Bar> mais je ne peux pas obtenir un shared_ptr<Bar>& quand je déréférencer mon iterator, ce qui est gênant, étant donné que l'utilisation de retourner seulement une référence à la vector sous-jacente (et non un const_reference) est de modifier effectivement la structure du vector et les objets qu'il contient.

Note 2 :
S'il vous plaît envisager refactoring. Il y a eu d'excellentes suggestions là.

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