Domanda

Considera il seguente codice di esempio:

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

Ho un problema come questo nel mio progetto attuale.Il codice cliente utilizza BarCollection, che memorizza i puntatori a Bars In d_foos che è dichiarato in FooCollection.Vorrei ora esporre la raccolta di puntatori a Bars al codice client.Potrei semplicemente dare al codice client l'accesso al vettore dei puntatori a Foos e lanciarli in puntatori a Bars nel codice client, ma sembra sbagliato poiché il client non deve saperlo Fool'esistenza.

Potrei anche definire a get() membro da cui recupera gli oggetti d_foos e li lancia, ma sembra piuttosto goffo.Preferibilmente, vorrei restituire d_foos semplicemente come a vector<shared_ptr<Bar> > &, ma non riesco a farlo.

Potrebbe anche essere che il mio progetto sia semplicemente sbagliato.Sembrava però la soluzione più naturale, come Bar è una specializzazione di Foo E BarCollection è una specializzazione di FooCollection e condividono funzionalità.

Potresti suggerire belle soluzioni da implementare getBars In BarCollection o alternative progettuali migliori?

Modificare:

Si scopre che il mio design era davvero pessimo.BarCollection non è una FooCollection, nonostante richieda tutte le funzionalità di FooCollection.La mia soluzione attuale basata sulle risposte seguenti, che è molto più pulita, è ora:

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

Grazie per tutti gli ottimi suggerimenti ed esempi!

È stato utile?

Soluzione

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

Altri suggerimenti

Vorrei suggerire esporre iteratori dalle vostre classi contenitore, invece del contenitore membro. In questo modo non importa ciò che il tipo di contenitore è.

Il problema è che si sta tentando di mix-and-match di due differenti, pretty-molto tipi indipendenti di polimorfismo in un modo che non funzionerà. La fase di compilazione, type-safe il polimorfismo di modelli non vi permetterà di sostituire un tipo di base per un tipo derivato. sistema di template C ++ 's non fa alcuna associazione tra

class<Foo>

e

class<Bar>

Un suggerimento potrebbe essere quello di creare un adattatore derivato Foo che avrebbe gettato giù alla classe corretta:

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

Si noti questa classe avrà lo stesso problema come un iteratore, un'operazione sul vettore può invalidare questa classe.

Un altro pensiero

L'utente non può rendersene conto, ma può essere perfettamente bene per tornare solo un vettore di Foo. L'utente del bar deve già avere piena conoscenza dei Foo, dal momento che includendo Bar.h devono ottenere foo.h attraverso Bar.h. Il motivo è che per la barra a ereditare da Foo, deve avere piena conoscenza della classe tramite Foo.h. Vorrei suggerire piuttosto che utilizzare la soluzione di cui sopra, se è possibile rendere Foo (o di una classe eccellente di Foo) una classe di interfaccia e passare intorno vettori di puntatori a quella classe di interfaccia. Questo è un modello abbastanza comunemente visto e non alzerà le sopracciglia che questa soluzione traballante mi è venuta potrebbe :). Poi di nuovo si può avere le tue ragioni. Buona fortuna in entrambi i casi.

La domanda è: perché l'hai fatto? Se si dà all'utente un insieme di puntatori a Bar, si assume, che ci sono solo bar in esso, in modo da memorizzare internamente i puntatori in una collezione di Foo non ha senso. Se si memorizzano diversi sottotipi di Foo nella vostra collezione di puntatore a Foo, non è possibile tornare come un insieme di puntatori a Bar, in quanto non tutti gli oggetti in ci sono bar. Nel primo caso, (si sa che hai solo bar) si dovrebbe usare un approccio basato su modelli come suggerito sopra. In caso contrario, è necessario ripensare, ciò che si vuole veramente.

Si può non sostituire questo con una collezione templatized su entrambi i Foo / Bar ?, qualcosa di simile

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

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

Hai un'esigenza particolare da avere BarCollection derivato da FooCollection?Perché generalmente a BarCollection non è UN FooCollection, di solito molte delle cose che possono essere fatte con a FooCollection non dovrebbe essere fatto con a BarCollection.Per esempio:

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

Ora abbiamo aggiunto a Foo opporsi a ciò che dovrebbe essere a BarCollection.Se la BarCollection tenta di accedere a questo elemento appena aggiunto e si aspetta che sia a Bar, accadranno tutti i tipi di cose brutte.

Quindi di solito vuoi evitarlo e non far derivare le tue classi di raccolta l'una dall'altra.Guarda anche domande Di contenitori di colata di tipi derivati ​​per ulteriori risposte su questo argomento...

Prima di tutto, parliamo di shared_ptr.Sai a proposito: boost::detail::dynamic_cast_tag ?

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

Questo è un modo molto pratico.Sotto la copertura esegue solo a dynamic_cast, niente di speciale lì tranne una notazione più semplice.Il contratto è lo stesso di quello classico:se l'oggetto puntato in realtà non è a Bar (o derivato da esso), ottieni un puntatore nullo.

Torniamo alla tua domanda:CODICE CATTIVO.

BarCollection non è un FooCollection, come detto sei quindi in difficoltà perchè potresti introdurre altri elementi nel vettore di puntatori that Bar quelli.

Non mi dilungherò su questo, però, perché questo va oltre la domanda in questione, e penso che noi (come coloro che cercano di rispondere) dovremmo trattenerci da questo.

Non puoi passare un riferimento, ma puoi passare a View.

Fondamentalmente, a View è un nuovo oggetto che funge da a Proxy a quello vecchio.È relativamente facile da usare Boost.Iteratori dall'esempio.

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

Il vero problema qui è ovviamente il reference morso.Ottenere un NEW shared_ptr l'oggetto è facile e consente di eseguire a dynamic_cast come richiesto.Ottenere un reference al ORIGINAL shared_ptr ma interpretato come il tipo richiesto...non è davvero quello che mi piace vedere nel codice.

Nota:
Potrebbe esserci un modo per fare meglio usando Boost.Fusion trasforma_vista lezione, ma non riuscivo a capirlo.

In particolare, utilizzando transform_view posso ottenere shared_ptr<Bar> ma non riesco a ottenere un shared_ptr<Bar>& quando dereferenzia il mio iteratore, il che è fastidioso dato che l'unico utilizzo è restituire un riferimento al sottostante vector (e non a const_reference) consiste nel modificare effettivamente la struttura del vector e gli oggetti che contiene.

Nota 2:
Si prega di considerare il refactoring.Ci sono stati ottimi suggerimenti lì.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top