• Effective C++ 条款35 考虑virtual函数以外的其他选择


    1. 在一个继承层次中,不同的类要实现同一接口的不同实现,最先想到的可能是虚函数,假设一个存在一个继承体系,这个集成体系中的每一层都需要一个名为fun函数,那么可能会像这样实现:

    clase Base{
    public:
        ...
        virtual fun(int num){...}
    private:
        ...
    }
    class Derived:public Base{
    public:
        ...
        virtual fun(int num){}
    private:
        ...
    }
    View Code

    但除了将fun设为虚函数,还有其他选择.

    2. "藉由Non-Virtual Interface手法实现Template Method模式"

         这一方法的基本思想是"令客户通过public non-virtual成员函数间接调用private virtual函数,成为non-virtual interface(NVI)手法",它是所谓Template Method设计模式(和template无关)的一个独特表现形式.即:

    class Base{
    public:
        ...
        void wrapper(int num){
        ...   //做一些事前工作
        fun(num);
        ...   //做一些事后工作
        }
    private:
        virtual void fun(int num){...}
    };
    class Derived:public Base{
    public:
        ...
    private:
        virtual void fun(int num){...}
    };
    View Code

    通过wrapper函数间接调用虚函数fun的优点在于在wraper函数可以在虚函数fun被调用之前做一些准备工作,在fun被调用之后做一些事后工作(正如注释)

    但以上代码将fun设为private,因此取消了Derived继承Base的fun的可能性,因此可以将fun设为protected(如果设为public将不能实施NVI手法)

    3. "藉由Function Pointers是吸纳Strategy模式"

        NVI手法只是对virtual函数做了一下修饰,加了一层wrapper函数,另一种替代方案是为继承层次中每一个类加一个函数指针,指向所需的函数,即:

    class Base{
    public:
        typedef void (*PtrF)(int);
        ...
        Base(PtrF para):ptr(para){...}
    private:
        PtrF ptr;
    }
    class Derived:public Base{
    public
        ...
        Derived(ptrF para):p(para){...}
    private:
        ...
    }
    View Code

    这样做提供了更大弹性:同一类型不同实体可以调用不同函数;同一实体可调用的函数可以在运行时期变更.

    缺点就是p所指向的函数不再是成员函数,如果想要访问类的non-public部分就只能弱化类的封装:将函数设为friend或将使用到的non-public类成员设为public或提供public接口获取non-public成员.

    4. "藉由tr1::function完成Strategy模式"

        3中策略提供了一定弹性,但不一定要使用指针,使用<functional>中的function类模板可以获得更大弹性(function模板的介绍见条款54),即:

    Base{
    public:
        function<void(int)> ptr;
        ...
    private:
        ...
    }
    Derived:public Base{
    public:
        ...
    private:
        ...
    }
    View Code

    此策略与3相比较的优点在于,ptr"可持有任何可调用物(callable entity,也就是函数指针,函数对象,或成员函数指针),只要其签名式兼容于需求端",因此提供了比3更大的弹性.ptr的方法像这样:

    void fun(int num){...}
    ptr=&fun;

    正如以上所言,ptr可以指向非成员函数,static成员函数,也可以指向函数对象,也可用函数指针对其赋值,但要使它指向成员函数还需要其他操作,因为成员函数有默认的this指针作为第一个参数,使用同一头文件中的bind函数模板可以解决这一问题,bind函数模板返回一个函数指针,bind的函数模板原型像这样:

    template <class Fn, class... Args>
    bind (Fn&& fn,Args&&... args);

    其中第一个参数是函数指针,接下来的参数是函数所需要的参数,如果参数是确定的值,那么bind返回的函数指针的同一位置的参数设为那个值,如果参数要保留,那么使用占位符tr1::placeholders::_1,tr1::placeholders::_2  ...  tr1::placeholders::_N,即:

    class Demo{
    public:
        int fun(int){...}
        ...
    private:
        ...
    }
    View Code
    Demo tmp;
    ptr=bind(&Demo::fun,tmp,tr1::placeholders::_1);
    View Code

    这样就把ptr绑定到成员函数了.

    关于function类模板和bind函数模板的具体使用方法见:

    http://blog.csdn.net/xiucaijiang/article/details/5999441

    http://www.cplusplus.com/reference/functional/bind/?kw=bind

    5. ”古典的Strategy模式"

    传统的Strategy做法会将fun做成一个分离的继承体系中的虚函数,即:

    class Fun{
    public:
        virtual void fun(int num){...}
        ...
    private:
        ...
    }
    class Base{
    public:
        Base(Fun*para):ptr(para){...}
        void fun(){ptr->fun();}
        ...
    private:
        Fun* ptr;
        ...
    }
    View Code

    这种策略的优点在于它提供了将一个既有的fun函数纳入使用的可能性,只要为Fun类集成体系新添加一个derived class即可.

  • 相关阅读:
    记中兴软件园一个月实训(四)
    MMORPG大型游戏设计与开发(服务器 AI 逻辑设定和状态结点)
    MMORPG大型游戏设计与开发(服务器 AI 事件)
    MMORPG大型游戏设计与开发(服务器 AI 控制器)
    MMORPG大型游戏设计与开发(服务器 AI 基础接口)
    MMORPG大型游戏设计与开发(服务器 AI 概述)
    MMORPG大型游戏设计与开发(服务器 游戏场景 动态场景与副本)
    MMORPG大型游戏设计与开发(服务器 游戏场景 掉落与网络连接)
    MMORPG大型游戏设计与开发(服务器 游戏场景 搜索机)
    MMORPG大型游戏设计与开发(服务器 游戏场景 聊天管道和寻路器)
  • 原文地址:https://www.cnblogs.com/reasno/p/4796134.html
Copyright © 2020-2023  润新知