使用模板和虚函数的技巧

时间:2010-06-30 18:53:26

标签: c++ templates

前段时间我了解了奇怪的重复模板模式(http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern),它让我想起了我用来实现事件队列缓存的技术。

基本思想是我们利用Base类指针来存储同类指针类型的容器。但是因为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 个答案:

答案 0 :(得分:3)

斯蒂芬在评论中指出,你所使用的是普通的多态性。在容器内部存储不同对象时,您只能使用Base中定义的接口。当然,除非您打算添加类型检查和向下转换以实际检索值。您可以使用不相关的对象进行有限的操作。

根据您实际想要实现的目标,您可以考虑使用其他解决方案,例如boost::any / boost::variant,如果您想要的是实际存储不相关的类型(在少数情况下这是有道理的 - 例如,电子表格中的单元格。

答案 1 :(得分:1)

  

有人给它命名了吗?

我认为它是一个不使用T的继承而实现的适配器pattern

  

有人关心批评吗?

你可以使用短模板功能代替这个类。或者您可以使用返回模板类的模板函数。模板功能可以自动猜测所需的类型 - 你可以省略的&lt;&gt;并且少打字。

答案 2 :(得分:1)

<强>尼斯。

你正在利用编译器的能力来生成模板化的派生类系列,你可以混合普通派生类真的很酷 (由您自己编写)具有模板专用派生类和编译器生成的类 (由模板实例化构建)。

class Base { ... };

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

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

class Derived2 : public Base { ... };

这可能很有用,但它不会以某种方式扩展多态术语,因为您仍然只能使用Base类接口。 < / p>

另外,你可以写一个普通的工厂,它有一些模板化的方法来生成子类并使用它来避免写new Derived1<std::string>...,但写一些像

std::string a;
Base* base = Factory.Create(a)