2011-12-20 13 views
5

Per essere onesti, non so davvero, come porre questa domanda, quindi per favore non essere arrabbiato :)C++ funzione nel genitore bambino cambio

In ogni caso, voglio avere le mutatori (setter) nella mia classe per restituire this per consentire jQuery-like a.name("something").address("somethingelse"); Ho una classe padre (Entity) e diversi childclasses (Client, Agent etc.). I mutatori per la maggior parte delle cose sono ereditati dalla classe Entity (come nome o indirizzo), ma restituiscono un oggetto Entity, quindi non posso richiamare i mutatori client su di essi.

In altre parole:

// name mutator 
Entity& Entity::name(const string& name) { 
    // [...] checks 
    _name = name; 
    return *this; 
} 

// budgetRange mutator 
Client& Client::budgetRange(const long int& range) { 
    // [...] checks 
    _budgetRange = range; 
    return *this; 
} 

poi quando lo chiamo io:

Client a; a.name("Dorota Adamczyk").budgetRange(50); 

Il compilatore (logicamente) dice che l'oggetto entità ha nessun membro budgetRange (perché il nome restituisce un Entity, non un cliente).

La mia domanda ora è: come potrei implementare qualcosa di simile? Ho pensato a un sovraccarico di tutte le funzioni entità nel childclasses, ma che non sarebbe bello e sarebbe contro l'idea di eredità :)

Grazie in anticipo per le vostre idee: D

+0

google anche per 'metodo concatenamento' e' named parametro idiom', che è quello che stai facendo.Si noti che, quando si eredita da 'Client' il codice si interromperà di nuovo usando la soluzione CRTP di seguito. – cheind

risposta

7

È necessario utilizzare CRTP.

template<class Derived> 
class Entity 
{ 
    Derived* This() { return static_cast<Derived*>(this); } 

public: 
    Derived& name(const string& name) 
    { 
     ... 
     return *This(); 
    } 
}; 

class Client : public Entity<Client> 
{ 
public: 
    Client& budgetRange(const long& range) 
    { 
     ...  
     return *this; 
    } 
}; 

Se si desidera utilizzare le funzioni virtuali, è possibile anche aggiungere classe astratta di base, in questo modo:

class AbstractEntity 
{ 
public: 
    virtual void foo() = 0; 

    virtual ~AbstractEntity(); 
}; 

template<class Derived> 
class Entity : AbstractEntity 
{...}; 
+0

Mi piace molto questa idea (specialmente la funzione This() ^^). Grazie mille, questo è quello che stavo cercando :) – Asmodiel

+0

Roba eccellente. Ho cercato un buon esempio di CRTP. Grazie. – sje397

+0

@ sje397 un altro buon esempio di CRTP è una classe di albero generica che fornisce implementazioni di nodi concreti tramite 'modello nodo di classe'. Ad esempio: 'D & node :: left()' può fornire il nodo concreto impl. In questo modo puoi inserire tutta la logica dell'albero nella classe nodo generica. – cheind

3

Il "modello curiosamente ricorsivo "il modello potrebbe aiutare qui; rendere la classe base di un modello, parametrizzato dalla classe derivata, sulla falsariga di:

template <typename Derived> 
struct Entity { 
    Derived & name(std::string const & name) { 
     // stuff 
     return static_cast<Derived&>(*this); 
    } 
}; 

struct Client : Entity<Client> { 
    Client & budget(long range) { 
     // stuff 
     return *this; 
    } 
}; 

Client().name("Mike").budget(50); // should compile 

Questo funziona solo se tutti i tipi di ereditano direttamente da Entity. Se hai bisogno che i tipi siano polimorfici (cioè condividono tutti una classe base comune), dovrai aggiungere un'altra classe base non modello e ereditare Entity.

+0

La ringrazio molto per la tua risposta, ma sceglierò Abyx 'come ha incluso l'idea di questo() :) Votato però. – Asmodiel

2

Ora che quasi tutto è già stato detto, voglio aggiungere un pezzo di risposta che permette di utilizzare il CRTP su più livelli di ereditarietà:

le implementazioni CRTP di cui sopra si rompono quando si vuole ereditare da Client, dal momento che sarà Derived fare riferimento a Client. Nel caso in cui si vuole essere in grado di portare il parametro linguaggio di nome su più livelli di ereditarietà usando il modello CRTP, è necessario codificare le classi in questo modo

template<class Derived> 
class Entity_T 
{ 
protected: 
    Derived* This() { return static_cast<Derived*>(this); } 
public: 
    Derived& name(const string& name) 
    { 
     ... 
     return *This(); 
    } 
}; 

template<class Derived> 
class Client_T : public Entity_T<Derived> 
{ 
    Derived& budgetRange(const long& range) 
    { 
     ...  
     return *This(); 
    } 
}; 

per fornire all'utente una versione gratuita-template di Client_T aggiungere

class Client : public Client_T<Client> {}; 

Indipendentemente dal fatto che valga o meno la base di codice ingrandita dipende interamente da voi. Nota che non ho compilato il codice sopra.

Problemi correlati