代码之家  ›  专栏  ›  技术社区  ›  Craig Wright

使用模板和虚拟函数的技术

  •  1
  • Craig Wright  · 技术社区  · 14 年前

    不久前,我了解了奇怪的重复模板模式( http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ,这让我想起了一种用于实现事件队列缓存的技术。

    基本思想是利用基类指针来存储同构指针类型的容器。但是,由于派生类是一个模板类,它存储一个类型为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 回复  |  直到 14 年前
        1
  •  3
  •   David Rodríguez - dribeas    14 年前

    正如斯蒂芬在他的评论中指出的,您使用的是简单的多态性。当您在容器内部存储不同的对象时,您只能使用在中定义的接口。 Base . 当然,也就是说,除非您打算添加类型检查和降级以实际检索值。对于不相关的对象,您可以执行的操作数量有限。

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

        2
  •  1
  •   SigTerm    14 年前

    有人给它命名了吗?

    我想是个适配器 pattern 在不使用T继承的情况下实现。

    有人愿意批评它吗?

    您可以使用短模板函数代替这个类。或者可以使用返回模板类的模板函数。模板函数可以自动猜测所需类型-因此您可以省略<gt;并减少键入。

        3
  •  1
  •   M. Williams    14 年前

    很好。

    您正在利用编译器的能力来生成派生类的模板化系列,实际上可以混合纯派生类是很酷的。 (自己写的) 使用模板专用的派生类和编译器生成的派生类 (作为模板实例化的结果生成)。

    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)