2010-06-30 3 views
1

Некоторое время назад я узнал о Curiously Recurring Template Pattern (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) и напомнил мне о технике, которую я использовал для реализации кеша очереди событий.Техника использования шаблонов и виртуальных функций

Основная идея заключается в том, что мы используем указатель базового класса для хранения контейнера с однородными типами указателей. Однако поскольку класс Derived является классом шаблона, в котором хранится элемент типа T, то мы действительно сохраняем список гетерогенных типов.

Мне было любопытно, если кто-нибудь видел эту технику, которая, возможно, интересна, и если да, если кто-то назвал ее? Кто-нибудь хочет критиковать его? Есть ли лучший способ достичь моего конца здесь?

Спасибо.

#include <iostream> 
#include <algorithm> 
#include <functional> 
#include <list> 
#include <string> 

class Base 
{ 
    public: 
     Base(){} 
     virtual ~Base(){} 

     virtual void operator()() = 0; 
}; 


template<typename C, typename T> 
class Derived : public Base 
{ 
    public: 

     Derived(C* c, T item) : consumer_(c), item_(item) {} 

     virtual void operator()() 
     { 
     consumer_->consume(item_); 
     } 

     C* consumer_; 
     T item_; 
}; 

class Consumer 
{ 
     bool postpone_; 
     std::list<Base*> cache_; 


    public: 
     Consumer() : postpone_(true) 
     { 
     } 

     void pause() 
     { 
     postpone_ = true; 
     } 

     void resume() 
     { 
     postpone_ = false; 

     const std::list<Base*>::iterator end = cache_.end(); 
     for (std::list<Base*>::iterator iter = cache_.begin(); 
       iter != end; 
       ++iter) 
     { 
      Base* bPtr = *iter; 
      bPtr->operator()(); 
      delete bPtr; 
     } 
     cache_.clear(); 
     } 

     void consume(int i) 
     { 
     if (postpone_) 
     { 
      std::cerr << "Postpone int.\n"; 
      cache_.push_back(new Derived<Consumer, int>(this, i)); 
     } 
     else 
     { 
      std::cerr << "Got int.\n"; 
     } 
     } 

     void consume(double d) 
     { 
     if (postpone_) 
     { 
      std::cerr << "Postpone double.\n"; 
      cache_.push_back(new Derived<Consumer, double>(this, d)); 
     } 
     else 
     { 
      std::cerr << "Got double.\n"; 
     } 
     } 
     void consume(char c) 
     { 
     if (postpone_) 
     { 
      std::cerr << "Postpone char.\n"; 
      cache_.push_back(new Derived<Consumer, char>(this, c)); 
     } 
     else 
     { 
      std::cerr << "Got char.\n"; 
     } 
     } 
}; 
static Consumer consumer; 



void destroy(Base* object) 
{ 
    delete object; 
} 


int main() 
{ 
    // Consumer is registered with something that sends events out to lots 
    // of different consumer types (think observer pattern). Also in the non-toy 
    // version consumer isn't being passed PODs, but various Event types. 
    consumer.consume(0); 
    consumer.consume(0.1f); 
    consumer.consume('x'); 

    consumer.resume(); 
} 

Выход:

Postpone int. 
Postpone double. 
Postpone char. 
Got int. 
Got double. 
Got char. 
+2

Я считаю, это называется «полиморфизм»;) – Stephen

ответ

1

кто назвал его?

Я думаю, что это адаптер pattern реализован без использования наследования от T.

Любой уход критиковать его?

Возможно, вы использовали короткую функцию шаблона вместо этого класса. Или вы можете использовать функцию шаблона, которая возвращает класс шаблона. Функция шаблона может автоматически угадать требуемые типы - су, вы можете опустить < > и сделать меньше ввода.

3

Вы используете простой полиморфизм, как указывает Стивен в своем комментарии. Хотя вы храните разные объекты внутри контейнера, вы ограничены использованием интерфейса, определенного в Base. Это, конечно, если вы не намерены добавлять проверки типов и понижения для фактического извлечения значений. Существует только ограниченное количество вещей, которые вы можете делать с несвязанными объектами.

В зависимости от того, что вы на самом деле хотите, чтобы достичь вы могли бы рассмотреть возможность использования других решений, как boost::any/boost::variant если то, что вы хотите на самом деле хранить несвязанные тип (в тех немногих случаях, когда это имеет смысл --cells в таблице, для пример).

+0

Большим полем для типов вариантов являются языковые мосты: COM, NPAPI, многие интерфейсы языка сценариев, ... –

1

Nice.

Вы, используя силу компилятора для создания шаблонного ряда производных классов, и это на самом деле здорово, что вы можете смешивать равнину производных классов (написанных самостоятельно) с использованием шаблонов специализированы производными классами и сгенерированного компилятором тех (построенный как результат создания шаблона).

class Base { ... }; 

template <typename Y> class Derived1 : public Base { ... }; 

template <specialization> 
class Derived1 : public Base { ... }; 

class Derived2 : public Base { ... }; 

Это может быть полезным, но это как-то не продлить срок полиморфизм, потому что вы все еще ограничены интерфейсом Base класса.

Кроме того, вы можете написать простой завод, который будет иметь некоторый шаблонный метод для создания подклассов и использовать его, чтобы избежать написания new Derived1<std::string>..., но написать что-то вроде

std::string a; 
Base* base = Factory.Create(a) 
Смежные вопросы