Domanda

Sembra che ho avuto un malinteso fondamentale su C ++: <

Mi piace la soluzione di contenitore polimorfico. Grazie mille, per averlo portato alla mia attenzione :)


Quindi, abbiamo bisogno di creare un oggetto di tipo contenitore relativamente generico. Capita anche di incapsulare alcune logiche aziendali. Tuttavia, abbiamo bisogno di archiviare dati essenzialmente arbitrari in questo contenitore - tutto, dai tipi di dati primitivi a classi complesse.

Quindi, si passerebbe immediatamente all'idea di una classe modello e si finirà con essa. Tuttavia, ho notato che il polimorfismo C ++ e i modelli non giocano bene insieme. Dato che esiste una logica complessa su cui dovremo lavorare, preferirei semplicemente attenermi a uno dei modelli o al polimorfismo e non tentare di combattere il C ++ facendolo fare entrambi.

Infine, dato che voglio fare l'uno o l'altro, preferirei il polimorfismo. Trovo molto più semplice rappresentare vincoli come & Quot; questo contenitore contiene tipi comparabili & Quot; - a la java.

Portandomi sull'argomento della domanda: al massimo, immagino di poter avere un " Contenitore " pura interfaccia virtuale che ha qualcosa di simile a " push (dati void *) e pop (dati void *) " (per la cronaca, in realtà non sto cercando di implementare uno stack).

Tuttavia, non mi piace molto void * al livello superiore, per non parlare della firma che cambierà ogni volta che voglio aggiungere un vincolo al tipo di dati con cui un contenitore concreto può lavorare.

Riassumendo: abbiamo contenitori relativamente complessi che hanno vari modi per recuperare elementi. Vogliamo essere in grado di variare i vincoli sugli elementi che possono entrare nei contenitori. Gli elementi dovrebbero funzionare con più tipi di contenitori (purché soddisfino i vincoli di quel particolare contenitore).

Modifica: dovrei anche menzionare che i contenitori stessi devono essere polimorfici. Questa è la mia ragione principale per non voler usare il C ++ basato su modelli.

Quindi - dovrei abbandonare il mio amore per le interfacce di tipo Java e scegliere i modelli? Dovrei usare void * e lanciare staticamente tutto? O dovrei andare con una definizione di classe vuota & Quot; Element & Quot; che non dichiara nulla e lo usa come la mia classe di livello superiore nella " Element " gerarchia?

Uno dei motivi per cui amo lo stack overflow è che molte delle risposte forniscono alcune informazioni interessanti su altri approcci che non avevo nemmeno preso in considerazione. Quindi grazie in anticipo per i tuoi approfondimenti e commenti.

È stato utile?

Soluzione

Non puoi avere una classe Contenitore radice che contiene elementi:

template <typename T>
class Container
{
public: 

   // You'll likely want to use shared_ptr<T> instead.
   virtual void push(T *element) = 0;
   virtual T *pop() = 0;
   virtual void InvokeSomeMethodOnAllItems() = 0;
};

template <typename T>
class List : public Container<T>
{
    iterator begin();
    iterator end();
public:
    virtual void push(T *element) {...}
    virtual T* pop() { ... }
    virtual void InvokeSomeMethodOnAllItems() 
    {
       for(iterator currItem = begin(); currItem != end(); ++currItem)
       {
           T* item = *currItem;
           item->SomeMethod();
       }
    }
};

Questi contenitori possono quindi essere passati polimorficamente:

class Item
{
public:
   virtual void SomeMethod() = 0;
};

class ConcreteItem
{
public:
    virtual void SomeMethod() 
    {
        // Do something
    }
};  

void AddItemToContainer(Container<Item> &container, Item *item)
{
   container.push(item);
}

...

List<Item> listInstance;
AddItemToContainer(listInstance, new ConcreteItem());
listInstance.InvokeSomeMethodOnAllItems();

Questo ti dà l'interfaccia del contenitore in un modo generico sicuro.

Se vuoi aggiungere vincoli al tipo di elementi che possono essere contenuti, puoi fare qualcosa del genere:

class Item
{
public:
  virtual void SomeMethod() = 0;
  typedef int CanBeContainedInList;
};

template <typename T>
class List : public Container<T>
{
   typedef typename T::CanBeContainedInList ListGuard;
   // ... as before
};

Altri suggerimenti

Puoi guardare usando un contenitore standard di boost: : any se stai memorizzando dati veramente arbitrari nel contenitore.

Sembra più che tu preferisca avere qualcosa come un boost :: ptr_container dove tutto ciò che può può essere archiviato nel contenitore deve derivare da un tipo di base e il contenitore stesso può darti solo riferimenti al tipo di base.

La cosa semplice è definire una classe base astratta chiamata Container e sottoclassarla per ogni tipo di oggetto che potresti voler conservare. Quindi è possibile utilizzare qualsiasi classe di raccolta standard (std::vector, std::list, ecc.) Per memorizzare i puntatori su <=>. Tieni presente che, dal momento che memorizzeresti i puntatori, dovresti gestirne l'allocazione / deallocazione.

Tuttavia, il fatto che sia necessaria un'unica raccolta per archiviare oggetti di tipi così selvaggiamente diversi è un'indicazione che qualcosa potrebbe non funzionare nel design dell'applicazione. Potrebbe essere meglio rivedere la logica aziendale prima di implementare questo contenitore super-generico.

Il polimorfismo e i modelli giocano molto bene insieme, se li usi correttamente.

In ogni caso, ho capito che vuoi archiviare un solo tipo di oggetti in ciascuna istanza del contenitore. In tal caso, utilizzare i modelli. Ciò ti impedirà di memorizzare per errore il tipo di oggetto errato.

Per quanto riguarda le interfacce contenitore: a seconda del progetto, forse sarai anche in grado di renderle modellate, e quindi avranno metodi come void push(T* new_element). Pensa a ciò che saprai sull'oggetto quando desideri aggiungerlo a un contenitore (di tipo sconosciuto). Da dove verrà l'oggetto in primo luogo? Una funzione che restituisce void*? Sai che sarà comparabile? Almeno, se tutte le classi di oggetti memorizzate sono definite nel codice, è possibile farle ereditare da un antenato comune, ad esempio Storable e utilizzare Storable* anziché void push(Storable* new_element).

Ora se vedi che gli oggetti verranno sempre aggiunti a un contenitore con un metodo come <=>, allora davvero non ci sarà alcun valore aggiunto nel rendere il contenitore un modello. Ma poi saprai che dovrebbe archiviare Storables.

Innanzitutto, i modelli e il polimorfismo sono concetti ortogonali e giocano bene insieme. Quindi, perché vuoi una struttura dati specifica? Che dire delle strutture di dati STL o boost (in particolare contenitore dei puntatori ) non funziona per te.

Data la tua domanda, sembra che tu stia abusando dell'eredità nella tua situazione. È possibile creare & Quot; vincoli " su ciò che accade nei contenitori, soprattutto se si utilizzano modelli. Tali vincoli possono andare oltre ciò che il compilatore e il linker ti daranno. In realtà è più imbarazzante per quel genere di cose con eredità e gli errori sono più probabilmente lasciati per il runtime.

Usando il polimorfismo, in pratica ti rimane una classe base per il contenitore e classi derivate per i tipi di dati. La classe di base / le classi derivate possono avere tutte le funzioni virtuali di cui hai bisogno, in entrambe le direzioni.

Naturalmente, ciò significherebbe che dovresti avvolgere anche i tipi di dati primitivi nelle classi derivate. Se riconsiderassi l'uso dei modelli in generale, è qui che utilizzerei i modelli. Crea una classe derivata dalla base che è un modello e usala per i tipi di dati primitivi (e altri in cui non hai bisogno di più funzionalità di quelle fornite dal modello).

Non dimenticare che potresti semplificarti la vita con i typedef per ciascuno dei tipi di template, specialmente se in seguito dovrai trasformarne uno in una classe.

Potresti anche voler dare un'occhiata a The Boost Concept Check Library (BCCL) che è progettata per fornire vincoli ai parametri del modello delle classi basate su modelli, in questo caso i contenitori.

E solo per ribadire ciò che altri hanno detto, non ho mai avuto problemi a mescolare polimorfismo e modelli e ho fatto cose abbastanza complesse con loro.

Non è possibile rinunciare a interfacce simili a Java e utilizzare anche i modelli.

scroll top