2013-03-05 6 views
5

Ho una domanda sullo stile. Ho una classe (nel mio caso un'opzione) che dipende dal valore di un oggetto esogeno (tasso di interesse). Il mio obiettivo è creare una classe base astratta per l'oggetto esogeno (Rate) in modo da poter costruire variazioni, ad esempio SimulatedRate o ConstantRate, che funzioneranno all'interno della mia classe, Option.C++: una classe astratta come membro

Tuttavia, sto trovando in C++, poiché ovviamente non posso creare un'istanza di una classe base astratta, devo memorizzare un puntatore o un riferimento alla classe base. La mia preoccupazione è che quando gli oggetti esogeni istanziati escono dall'ambito al di fuori della classe dipendente, la mia classe dipendente punta alla spazzatura.

Esiste un modo ragionevole per utilizzare il polimorfismo per questo problema in C++?

mio codice corrente:

class Dependent 
{ 
public: 
    Dependent(const Exogenous& exo) : exo_(exo) {} 
    double getSomething() const { exo_.interfaceMethod(); } 
private: 
    Exogenous& exo_; 
} 

class Exogenous 
{ 
public: 
    virtual double interfaceMethod() const=0; 
} 

class ExogenousVariationA 
{ 
public: 
    virtual double interfaceMethod() const { return resultA; } 
} 

class ExogenousVariationB 
{ 
public: 
    virtual double interfaceMethod() const { return resultB; } 
} 
+0

Shall 'Dependent' necessariamente mantenere un riferimento a' Exogenous', o può derivare da esso? Vale a dire. Devi istanziare entrambe le classi separatamente? – didierc

+0

In primo luogo, gli oggetti polimorfici sono sempre trattenuti dal puntatore/riferimento. Anche in altre lingue come Java questo è il caso. In secondo luogo, poiché si tratta di C++, è necessario utilizzare sempre la corretta gestione degli oggetti C++/RAII, che per puntatori indica puntatori intelligenti. – bames53

risposta

3

sftrabbit ha qualche buon consiglio, a cui mi piacerebbe aggiungere:

  • si potrebbe creare un metodo di virtualclone() nella classe base astratta (non è un virtuale di classe di base - che è qualcosa di completamente diverso) ; tale metodo sarebbe implementato nelle classi di tasso di interesse derivato, restituendo un puntatore a un nuovo oggetto di tasso di interesse indipendente che può essere posseduto dall'Opzione; questo è particolarmente utile se gli oggetti contengono dati che cambiano man mano che lo usi (ad es. da calcoli o caching)
  • probabilmente non lo vuoi, ma con i puntatori condivisi di std/boost è anche possibile chiedere un puntatore debole a l'oggetto condiviso ...in questo modo è possibile verificare se i "proprietari" dell'oggetto (che non comprenderà voi) hanno già finito con esso e innescato la sua distruzione

Separatamente, per usare il polimorfismo runtime tuoi ExogenousVariationA e ~ B classi devono realtà derivare da Exogenous e il metodo che si desidera inviare polimorficamente deve essere virtual. Che assomiglia a questo:

class Exogenous 
{ 
public: 
    virtual double interfaceMethod() const=0; 
} 

class ExogenousVariationA : public Exogenous 
{ 
public: 
    double interfaceMethod() const { return resultA; } 
} 
+0

Perfetto! Il metodo clone() farà esattamente quello di cui ho bisogno. Ci scusiamo per il codice di esempio scadente. – curltron

4

la vostra preoccupazione è valido. Dato che stai memorizzando su un riferimento un oggetto passato dal client, hai fiducia nel fatto che il client mantenga vivo l'oggetto mentre ne hai bisogno. Questo può facilmente portare a problemi. Ovviamente, lo stesso sarebbe vero se si usassero puntatori non elaborati su oggetti allocati dinamicamente. Se il client fa delete sull'oggetto prima di averlo fatto, ancora una volta hai un problema.

La soluzione è costringere il cliente a darti un qualche tipo di responsabilità per la durata dell'oggetto. Il modo per farlo è chiedere un puntatore intelligente. A seconda del tuo problema, potresti volere uno std::unique_ptr o std::shared_ptr. Usa il primo se vuoi assumere la proprietà dal client o quest'ultimo se vuoi condividere la proprietà con loro. Diciamo che si sceglie std::unique_ptr, si dovrebbe quindi definire la classe Dependent come:

class Dependent 
{ 
public: 
    Dependent(std::unique_ptr<Exogenous> exo) : exo_(std::move(exo)) {} 
    double getSomething() const { exo_->interfaceMethod(); } 
private: 
    std::unique_ptr<Exogenous> exo_; 
} 

il cliente sarebbe utilizzare questo modo:

std::unique_ptr<Exogenous> ptr(new ExogenousVariationA()); 
Dependent dep(std::move(ptr)); 

Ora, quando il client passa il std::unique_ptr a voi,' ti sta dando la proprietà dell'oggetto. L'oggetto verrà distrutto solo quando il tuostd::unique_ptr viene distrutto (che sarà quando il tuo Dependent viene distrutto, poiché è un membro).

In alternativa, se si prende uno std::shared_ptr, l'oggetto verrà distrutto una volta che sia il client sia il tuo std::shared_ptr vengono distrutti.

+0

Speravo di poter evitare i puntatori intelligenti, ma questa è comunque una soluzione eccellente. Grazie. – curltron