Quale sarebbe il modo più sicuro per memorizzare gli oggetti delle classi derivate da un'interfaccia comune in un contenitore comune?

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

Domanda

Mi piacerebbe gestire un gruppo di oggetti di classi derivate da una classe di interfaccia condivisa in un contenitore comune.

Per illustrare il problema, diciamo che mi sto costruendo un gioco che conterrà diversi attori. Chiamiamo il IActor dell'interfaccia e derivano Enemy e Civilian da esso.

Ora, l'idea è quella di avere il mio gioco ciclo principale essere in grado di fare questo:

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

e

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

... o qualcosa del genere. In questo esempio, ovviamente, non funzionerà, ma che è più o meno la ragione per cui mi sto chiedendo qui.

Mi piacerebbe sapere: Quale sarebbe il migliore, più sicuro, modo più alto livello per gestire tali oggetti in un contenitore eterogeneo comune? So di una varietà di approcci (boost :: Qualsiasi, * vuoto, classe del gestore con boost :: shared_ptr, Boost.Pointer Container, dynamic_cast), ma non riesco a decidere quale sarebbe il modo di andare qui.

Inoltre vorrei sottolineare che io voglio stare lontano il più lontano possibile dalla gestione della memoria manuale o puntatori nidificate.

Guida molto apprezzato:).

È stato utile?

Soluzione

Come avete indovinato è necessario memorizzare gli oggetti come puntatori.
Io preferisco usare i contenitori puntatore spinta (piuttosto che un normale contenitore di puntatori intelligenti).

La ragione di questo è l'accesso spinta contenitore di PTR gli oggetti come se fossero oggetti (che tornano i riferimenti), piuttosto che puntatori. Questo rende più facile da usare funtori standard e algoritmi sui contenitori.

Lo svantaggio di puntatori intelligenti è che si sta condividendo la proprietà.
Questo non è ciò che si vuole veramente. Si vuole la proprietà di essere in un unico luogo (in questo caso il contenitore).

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

e

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

Altri suggerimenti

Per risolvere il problema, che si è detto, anche se si sta andando nella giusta direzione, ma si sta facendo nel modo sbagliato. Questo è quello che si avrebbe bisogno di fare

  • Definire una classe di base (che si sta già facendo) con funzioni virtuali che sarebbe essere invocati classi derivate Enemy e Civilian nel tuo caso.
  • È necessario scegliere un contenitore adeguato con memorizzerà il vostro oggetto. Avete preso un std::vector<IActor> che non è una buona scelta, perché
    • In primo luogo quando si aggiungono oggetti al vettore che sta portando ad opporsi affettare. Ciò significa che solo la parte IActor di Enemy o Civilian viene memorizzato anziché l'intero oggetto.
    • In secondo luogo è necessario chiamare funzioni a seconda del tipo di oggetto (virtual functions), che può avvenire solo se si utilizzano i puntatori.

Sia del motivo precedente punto al fatto che è necessario utilizzare un contenitore che può contenere puntatori, qualcosa come std::vector<IActor*>. Ma una scelta migliore sarebbe quella di utilizzare container of smart pointers che vi farà risparmiare dai problemi di gestione della memoria. È possibile utilizzare uno qualsiasi dei puntatori intelligenti a seconda delle vostre necessità (ma non auto_ptr)

Questo è ciò che il codice sarà simile

// 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()));

e

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

Il che è più o meno simile al vostro codice originale tranne che per i puntatori intelligenti parte

La mia reazione immediata è che si dovrebbe memorizzare puntatori intelligenti nel contenitore, e assicurarsi che la classe base definisce abbastanza (puri) metodi virtuali che non hai mai bisogno di dynamic_cast di nuovo alla classe derivata.

Se si desidera che il contenitore di possedere esclusivamente gli elementi in esso, utilizzare un contenitore puntatore Boost: sono progettati per quel lavoro. In caso contrario, utilizzare un contenitore di shared_ptr<IActor> (e naturalmente usarli correttamente, il che significa che tutti coloro che hanno bisogno di condividere la proprietà utilizza shared_ptr).

In entrambi i casi, assicurarsi che il distruttore di IActor è virtuale.

void* richiede di fare gestione manuale della memoria, in modo che è fuori. Boost.Any è eccessivo quando i tipi sono legati per eredità -. Polimorfismo di serie fa il lavoro

Se avete bisogno di dynamic_cast o non è un problema ortogonali - se gli utenti del contenitore devono solo l'interfaccia IActor, e si sia (a) effettuare tutte le funzioni della interfaccia virtuale, oppure (b) utilizzare la non interfaccia linguaggio virtuale, quindi non è necessario dynamic_cast. Se gli utenti del contenitore sanno che alcuni degli oggetti IActor sono "realmente" i civili, e vogliono fare uso di cose che sono nell'interfaccia civile, ma non IActor, allora si avrà bisogno calchi (o una riprogettazione).

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