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即可.

 

posted @ 2015-09-10 15:20  Reasno  阅读(422)  评论(0编辑  收藏  举报