Техника использования шаблонов и виртуальных функций - PullRequest
1 голос
/ 30 июня 2010

Некоторое время назад я узнал о шаблоне Curily Recurring Template (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.

Ответы [ 3 ]

3 голосов
/ 30 июня 2010

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

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

1 голос
/ 30 июня 2010

Хорошо.

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

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)
1 голос
/ 30 июня 2010

кто-нибудь назвал его?

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

Anyoneхотите его критиковать?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...