Modern C++ design 第五章

本章介绍泛化仿函数,是一种可以将C++所允许的任何处理请求封装起来, 并可以获得“型别安全性质”的高级对象。 特性:
可封装任何处理请求,比如函数指针,成员函数指针,仿函数, 其它泛化仿函数,并连同参数。
具备类型安全, 不会讲错误的类型匹配到错误的函数。
具备值语义, 即支持拷贝,赋值, 传值。

泛化仿函数具有的特性是封装处理请求,和设计模式中的Command模式意义相近,都可以延迟执行。 泛化仿函数可以实现Command.

仿函数是通过重载operator()实现的, C++语言中支持operator()的构件有:
C-like function
C-like pointer to function
reference to function, 本质上和const pointer to function类似
functor, 仿函数, 即自定义了operator()的对象
operator.*或operator->*的结果。 表达式右侧是一个pointer to member function
Constructor-call
所有上述构件都可以通过()调用。
要时间泛化仿函数Functor, 首先需要解决参数的问题, 不同的函数, 返回类型以及参数的数量都不同, 这里用以解决这个问题的技术是, 实现一个FunctorImpl, 一个仿函数的具体实现, Functor是FunctorImpl的Wrapper. 用FunctorImpl解决上述问题。
因为一般而言, 最多只会有十来个参数, 函数库的FunctorImpl分别特化了0-15个不同的类型,解决了这个问题, 如果函数参数不超过15个的话,就都可以利用。 定义类似如下:
ThreadingModel是线程模型, 为了处理多线程, 如果不需要线程处理, 可以不予理会,自然会用缺省行为。
    template <typename R, class TList,
        template <class, class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL>
    class FunctorImpl;
    template <typename R, template <class, class> class ThreadingModel>

    class FunctorImpl<R, NullType, ThreadingModel>// R是返回类型
        : public Private::FunctorImplBase<R, ThreadingModel>
    {
    public:
        typedef R ResultType;
        virtual R operator()() = 0;// 之所以写为virtual函数, 是方便继承。
    };
    template <typename R, typename P1, template <class, class> class ThreadingModel>
        class FunctorImpl<R, Seq<P1>, ThreadingModel>
        : public Private::FunctorImplBase<R, ThreadingModel>
    {
    public:
        typedef R ResultType;
        typedef typename TypeTraits<P1>::ParameterType Parm1;
        virtual R operator()(Parm1) = 0;
    };
以此类推, 特化十五个FunctorImpl。
Functor 利用了模板的一个特性,即如果不用的内容就不生成, 定义了一系列不同参数的operator()重载。
然后将一个FunctorImpl*的指针传给Functor即可。
不过实际过程中, 需要包装的行为未必都能从FunctorImp派生, 所以需要一个FunctorHandler, 这个东西从FunctorImpl派生, 并且可以包装一个行为。 实现:

   template <class ParentFunctor, typename Fun>
    class FunctorHandler
        : public ParentFunctor::Impl
    {
 typedef typename ParentFunctor::Impl Base;

    public:
        typedef typename Base::ResultType ResultType;
        typedef typename Base::Parm1 Parm1;
        typedef typename Base::Parm2 Parm2;
       
        FunctorHandler(const Fun& fun) : f_(fun) {}
       
        LOKI_DEFINE_CLONE_FUNCTORIMPL(FunctorHandler)
              
        ResultType operator()()
        { return f_(); }

        ResultType operator()(Parm1 p1)
        { return f_(p1); }
       
        ResultType operator()(Parm1 p1, Parm2 p2)
        { return f_(p1, p2); }       
    private:
        Fun f_;
    };
同时这个实现支持一般的函数指针, 并支持C++的类型推导机制。
不过当有不同的重载函数的时候,  包装的函数必须明确制定类型,否则会不能识别。

Pointer to member function也是一个额外的主题, 原因是当调用成员函数指针的时候, 不仅需要函数指针,还需要一个调用者, 即一个对象或者对象的引用。为此定义一个MemFunHandler, 也从FunctorImpl派生,满足匹配Functor的要求。 出去需要多保存一个对象的指针, 其它区别不大。
   template <class ParentFunctor, typename PointerToObj,
        typename PointerToMemFn>
    class MemFunHandler : public ParentFunctor::Impl
    {
        typedef typename ParentFunctor::Impl Base;

    public:
        typedef typename Base::ResultType ResultType;
        typedef typename Base::Parm1 Parm1;
        typedef typename Base::Parm2 Parm2;

        MemFunHandler(const PointerToObj& pObj, PointerToMemFn pMemFn)
        : pObj_(pObj), pMemFn_(pMemFn)
        {}
       
        LOKI_DEFINE_CLONE_FUNCTORIMPL(MemFunHandler)

        ResultType operator()()
        { return ((*pObj_).*pMemFn_)(); }

        ResultType operator()(Parm1 p1)
        { return ((*pObj_).*pMemFn_)(p1); }
       
        ResultType operator()(Parm1 p1, Parm2 p2)
        { return ((*pObj_).*pMemFn_)(p1, p2); }
       

    private:
        PointerToObj pObj_;
        PointerToMemFn pMemFn_;
    };

绑定, 绑定实现的目的是可以绑定函数调用的一个参数, 类似stl中的Binder1st和Bind1st. 实现方式是邦定第一个参数的值, 其它的类型继续保留。 当然,也需要派生自FunctorImpl. 实现如下: 其中BindFirst是一个包装函数, 为了可以更方便的使用邦定。BinderFirstTraits是一个用来分析类型的traits, 这里不需要过多关心。
   template <class OriginalFunctor>
    class BinderFirst
        : public Private::BinderFirstTraits<OriginalFunctor>::Impl
    {
        typedef typename Private::BinderFirstTraits<OriginalFunctor>::Impl Base;
        typedef typename OriginalFunctor::ResultType ResultType;

        typedef typename OriginalFunctor::Parm1 BoundType;

        typedef typename Private::BinderFirstBoundTypeStorage<
                             typename Private::BinderFirstTraits<OriginalFunctor>
                             ::OriginalParm1>
                         ::RefOrValue
                BoundTypeStorage;
                       
        typedef typename OriginalFunctor::Parm2 Parm1;
        typedef typename OriginalFunctor::Parm3 Parm2;


    public:
       
        BinderFirst(const OriginalFunctor& fun, BoundType bound)
        : f_(fun), b_(bound)
        {}

        LOKI_DEFINE_CLONE_FUNCTORIMPL(BinderFirst)

        // operator() implementations for up to 15 arguments
               
        ResultType operator()()
        { return f_(b_); }

        ResultType operator()(Parm1 p1)
        { return f_(b_, p1); }
       
        ResultType operator()(Parm1 p1, Parm2 p2)
        { return f_(b_, p1, p2); }
   
 
       
    private:
        OriginalFunctor f_;
        BoundTypeStorage b_;
    };

    template <class Fctor>
    typename Private::BinderFirstTraits<Fctor>::BoundFunctorType
    BindFirst(
        const Fctor& fun,
        typename Fctor::Parm1 bound)
    {
        typedef typename Private::BinderFirstTraits<Fctor>::BoundFunctorType
            Outgoing;
       
        return Outgoing(std::auto_ptr<typename Outgoing::Impl>(
            new BinderFirst<Fctor>(fun, bound)));
    }
链接命令, 类似MacroCommand, 可以实现一系列命令的集合.

    template <class Fun1, class Fun2>
    Fun2 Chain(
        const Fun1& fun1,
        const Fun2& fun2)
    {
        return Fun2(std::auto_ptr<typename Fun2::Impl>(
            new Chainer<Fun1, Fun2>(fun1, fun2)));
    }
最后, 提供了两个优化, 第一个是对象copy的优化, 内部将所有的对象的参数传递都实现为reference. 利用了第二章的TypeTraits.
第二个是堆分配, 因为小对象的内存分配效率不高, 所以将FunctorImpl从第四章介绍了SmallObject派生, 利用小对象分配器。
利用Functor可以轻松实现Undo和Redo. 将命令压入栈中即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值