2011-12-20 1 views
5

Честно говоря, я не знаю, как задать этот вопрос, так что, пожалуйста, не злись :)C++ функция родительского возвращения ребенка

Во всяком случае, я хочу, чтобы иметь мутаторов (сеттеры) в моем классе, чтобы вернуть this, чтобы разрешить jQuery-like a.name("something").address("somethingelse"); У меня есть родительский класс (Entity) и несколько дочерних классов (Client, Agent etc.). Мутаторы для большинства вещей наследуются от класса Entity (например, имя или адрес), но они возвращают объект Entity, поэтому я не могу вызывать на них клиентские мутаторы.

Другими словами:

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

тогда, когда я называю его:

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

Компилятор (логически) говорит, что объект Entity не имеет ни одного члена budgetRange (потому что имя возвращает Entity, а не Клиентом).

Мой вопрос сейчас: как я мог реализовать что-то подобное? Я думал о перегрузке всех функций сущностей в childclasses, но это не было бы хорошо и было бы против идеи наследования :)

Спасибо заранее за ваши идеи: D

+0

google также для 'method chaining' и' named parameter idiom', что вы делаете.Обратите внимание, что при наследовании от «клиента» ваш код снова сломается с использованием CRTP-решения ниже. – cheind

ответ

7

Вы должны использовать 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; 
    } 
}; 

Если вы хотите использовать виртуальные функции, вы можете также добавить абстрактный базовый класс, например:

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

    virtual ~AbstractEntity(); 
}; 

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

Мне очень нравится эта идея (особенно функция This() ^^). Большое спасибо, вот что я искал :) – Asmodiel

+0

Отличный материал. Я искал хороший пример CRTP. Благодарю. – sje397

+0

@ sje397 Еще один хороший пример CRTP - это общий древовидный класс, который предоставляет конкретные реализации узлов через 'template класс node'. Например: 'D & node :: left()' может предоставить конкретный узел impl. Таким образом, вы можете вставить всю древовидную логику в общий класс узлов. – cheind

3

«любопытно рекурсивный шаблон «шаблон может помочь здесь; сделать базовый класс шаблон, параметрирован производным классом, вдоль линий:

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 

Это будет работать, только если все ваши типы наследуют непосредственно от Entity. Если вам нужны типы, которые должны быть полиморфными (т. Е. Все имеют общий базовый класс), тогда вам нужно будет добавить другой базовый класс без шаблона и наследовать от него Entity.

+0

Большое вам спасибо за ответ, но я выберу Abyx 'one, поскольку он включил эту идею() :) Приобретено. – Asmodiel

2

Теперь, когда почти все уже было сказано, я хочу, чтобы добавить часть ответа что позволяет использовать CRTP на несколько уровней наследования:

Вышеуказанные реализации CRTP ломаются, когда один хочет, чтобы наследовать от Client, так как Derived будет относиться к Client. В случае, если вы хотите, чтобы иметь возможность носить с именем параметра идиомы над несколькими уровнями наследования, используя CRTP шаблон, вы должны кодировать классы как так

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

Чтобы предоставить пользователю шаблон-бесплатную версию Client_T добавить

class Client : public Client_T<Client> {}; 

Независимо от того, стоит ли увеличить увеличенную базу кода, зависит только от вас. Обратите внимание, что я не составил код выше.

 Смежные вопросы

  • Нет связанных вопросов^_^