Frage

Betrachten Sie den folgenden Beispielcode:

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...
    }
};

Ich habe ein solches Problem in meinem aktuellen Projekt. Der Client -Code verwendet BarCollection, was Zeiger auf speichert Bars in d_foos das wird in deklariert FooCollection. Ich möchte jetzt die Sammlung von Zeigern dem Client -Code an Bars aussetzen. Ich könnte dem Client -Code nur Zugriff auf den Vektor der Zeiger geben Foos und wirken Sie diese an Zeiger auf Bars im Client -Code, aber das fühlt sich falsch an, da der Kunde nichts wissen muss FooExistenz.

Ich könnte auch a definieren get() Mitglied, das Objekte abruft d_foos Und wirkt sie, aber das fühlt sich ziemlich ungeschickt an. Vorzugsweise möchte ich nur d_foos als zurückgeben vector<shared_ptr<Bar> > &, aber ich kann das nicht tun.

Es könnte auch sein, dass mein Design einfach falsch ist. Es schien jedoch für die natürliche Lösung zu sein, als wie Bar ist eine Spezialisierung von Foo und BarCollection ist eine Spezialisierung von FooCollection Und sie teilen Funktionen.

Könnten Sie nette Lösungen für die Implementierung vorschlagen? getBars in BarCollection Oder bessere Designalternativen?

Bearbeiten:

Es stellte sich heraus, dass mein Design in der Tat schlecht war. Barcollection ist keine Foocollection, obwohl die gesamte Funktionalität von Foocollection erforderlich ist. Meine aktuelle Lösung basiert auf den folgenden Antworten - was viel sauberer ist - jetzt:

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.
};

Vielen Dank für all die hervorragenden Vorschläge und Beispiele!

War es hilfreich?

Lösung

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> {
};

Andere Tipps

Ich würde empfehlen, Iteratoren aus Ihren Containerklassen anstelle des Mitgliedsbehälters auszusetzen. Auf diese Weise spielt es keine Rolle, was der Containertyp ist.

Das Problem ist, dass Sie versuchen, zwei verschiedene, ziemlich unabhängige Arten von Polymorphismus auf eine Weise zu mischen, die nicht funktioniert. Mit dem Typ-Safe-Polymorphismus von Templatten können Sie nicht einen Basistyp für einen abgeleiteten Typ ersetzen. Das Vorlagensystem von C ++ macht keinen Zusammenhang zwischen

class<Foo>

und

class<Bar>

Ein Vorschlag könnte darin bestehen, einen von Foo abgeleiteten Adapter zu erstellen, der auf die richtige Klasse abweist:

 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);
     }
 };

Beachten Sie, dass diese Klasse das gleiche Problem wie ein Iterator hat. Der Betrieb im Vektor kann diese Klasse ungültig machen.

Ein weiterer Gedanke

Sie werden es vielleicht nicht merken, aber es ist vielleicht vollkommen in Ordnung, nur einen Vektor von Foo zurückzugeben. Der Nutzer von Bar muss bereits über volle Kenntnisse in Foo verfügen, da sie durch Bar.H Foo.h durch Bar.H. Der Grund dafür ist, dass die Bar, um von Foo von Foo zu erben, über Foo.h. Ich würde vorschlagen, anstatt die obige Lösung zu verwenden, wenn es möglich ist, Foo (oder eine Superklasse von Foo) zu einer Schnittstellenklasse zu machen und um Vektoren von Zeigern an diese Schnittstellenklasse zu gelangen. Dies ist ein ziemlich häufig gesehenes Muster und hebt nicht die Augenbrauen, die diese wackelige Lösung, die ich mir ausgedacht habe, vielleicht :). Andererseits haben Sie möglicherweise Ihre Gründe. Viel Glück oder so.

Die Frage ist, warum Sie das tun sollten? Wenn Sie dem Benutzer eine Sammlung von Zeigern in die Bar geben, gehen Sie davon aus, dass nur Bars darin enthalten sind, so dass das interne Speichern der Zeiger in einer Sammlung zu Foo keinen Sinn ergibt. Wenn Sie verschiedene Subtypen von Foo in Ihrer Zeigersammlung auf FOO speichern, können Sie es nicht als Sammlung von Zeigern in Bar zurückgeben, da nicht alle Objekte dort Bars sind. Im ersten Fall (Sie wissen, dass Sie nur Balken haben) sollten Sie wie oben vorgeschlagen einen Vorlagenansatz verwenden. Ansonsten musst du überdenken, was du wirklich willst.

Können Sie dies nicht durch eine Sammlung ersetzen, die in der beiden Foo / Bar templatisiert ist ?, Etwas wie diese

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

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

Haben Sie ein besonderes Bedürfnis zu haben? BarCollection abgeleitet von FooCollection? Weil im Allgemeinen a BarCollection ist nicht a FooCollection, normalerweise viele Dinge, die mit a gemacht werden können FooCollection sollte nicht mit a gemacht werden BarCollection. Zum Beispiel:

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

Jetzt haben wir a hinzugefügt Foo Objekt gegen das, was ein sein soll BarCollection. Wenn die BarCollection versucht, auf dieses neu hinzugefügte Element zuzugreifen und erwartet, dass es a ist Bar, Es werden alle möglichen hässlichen Dinge passieren.

Normalerweise möchten Sie dies vermeiden und haben Ihre Sammelklassen nicht voneinander abgeleitet. Siehe auch Fragen um Container gießen von abgeleiteten Typen für weitere Antworten zu diesem Thema ...

Lassen Sie uns zunächst darüber sprechen shared_ptr. Weißt du Bescheid über: boost::detail::dynamic_cast_tag ?

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

Dies ist ein sehr praktischer Weg. Unter dem Cover führt es nur eine durch dynamic_cast, nichts Besonderes dort als eine einfachere Notation. Der Vertrag ist der gleiche wie der Klassiker: Wenn das Objekt darauf hingewiesen hat Bar (oder abgeleitet davon), dann bekommen Sie einen Nullzeiger.

Zurück zu Ihrer Frage: schlechter Code.

BarCollection ist kein FooCollection, wie bereits erwähnt, dass Sie daher in Schwierigkeiten sind, weil Sie andere Elemente im Vektor von Zeigern einführen können, die Bar Einsen.

Ich werde das jedoch nicht ausdehnen, denn dies geht über die Frage hinaus, und ich denke, dass wir (als diejenigen, die versuchen zu antworten) uns davon zurückhalten sollten.

Sie können keine Referenz übergeben, aber Sie können a passieren View.

Grundsätzlich a View ist ein neues Objekt, das als Proxy zum alten. Es ist relativ einfach mit Boost.iteratoren Aus Beispiel.

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

Das eigentliche Problem hier ist natürlich das reference bisschen. A NEW shared_ptr Objekt ist einfach und ermöglicht es, eine auszuführen dynamic_cast nach Bedarf. A reference zum ORIGINAL shared_ptr Aber als der erforderliche Typ interpretiert ... ist wirklich nicht das, was ich im Code sehe.

Notiz:
Es könnte eine Möglichkeit geben, mit Boost.fusion besser zu machen Transformation_View Klasse, aber ich konnte es nicht herausfinden.

Insbesondere verwenden transform_view ich kann erhalten shared_ptr<Bar> Aber ich kann keine bekommen shared_ptr<Bar>& Wenn ich der Dereferenz meines Iterators, was ärgerlich ist, da die einzige Verwendung der Rückgabe eines Verweis vector (und nicht a const_reference) ist tatsächlich die Struktur der Struktur der vector und die Objekte, die es enthält.

Anmerkung 2:
Bitte beachten Sie, dass Refactoring. Dort gab es hervorragende Vorschläge.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top