不用虚函数机制实现运行时多态

今天在豆瓣看到一个好友讨论构造函数、运算符重载等等相关概念。于是想到C++里面实现运行时多态只能用虚函数机制吗?一直以来从各种学习材料上都是这么学的:提到多态要知道有编译时和运行时之分;运行时多态和晚期联编要用到虚函数机制……等等,好像这俩概念是连体的一样。实际上C++的多态机制只是用vtable和虚函数帮我们做了很多事情而已,不用虚函数,也可以实现多态——当然,这样一来有些功能可能就用不了了,比如我曾经在概念上犯过SB的dynamic_cast。

那么要怎么实现呢?假设手头有两个类,Base和从它继承出来的Derive,两者都定义了名为Foo()的成员函数。我们期望下面的调用可以走到Derive的版本里面:

Base* d = new Derive();
d->Foo();

最最简单的思路:

(static_cast<Derive*>(d))->Foo();

怎么样,确实达到了要求,只不过……有些犯规的感觉。

那么换个思路,利用函数指针:

class Base
{
    public:
    void Foo()
    {
        if ( _pVFunc == NULL )
            cout<<"Base::Foo"<<endl;
        else
            _pVFunc();
    }
 
    protected:
    typedef void (*V_FUNC)();
    V_FUNC _pVFunc;
};
 
class Derive : public Base
{
    public:
    void Foo() { cout<<"Derive::Foo"<<endl; }
    Derive() { _pVFunc = (V_FUNC)&Derive::Foo; }
};

略寒碜,而且那个_pVFunc语义非常不明确,美化一下:

class Base
{
    public:
    typedef void (Base::*V_FUNC)();
    void Foo() { (_caller->*_foo)(); }
    Base() { SetFunc( this, &Base::DoFoo ); }
    protected:
    void SetFunc( Base* caller, V_FUNC func) { _caller = caller; _foo = func; }
    Base* _caller;
    V_FUNC _foo;
    private:
    void DoFoo() { cout<<"Base::Foo"<<endl; }
};
 
class Derive : public Base
{
    public:
    Derive() { SetFunc( this, (V_FUNC)&Derive::DoFoo ); }
    private:
    void DoFoo() { cout<<"Derive::Foo"<<endl; }
};

等等,这似乎完全不算是美化啊,依然寒碜到家——只是调用者和函数的关系更加明确了。指向成员函数的指针这种东东可读性真的不够好。目前为止穷尽我的脑细胞也就能想到上面这两类(如果第一种也算)方法来。用函数指针实现的版本虽然调用上令人满意,但实现上问题实在太多:指针类型要跟着函数原型走,新增一种其他参数、返回值的函数就要重新写一个;和类型绑定,无法扩展;SetFunc丑陋无比。

于是放狗发现StackOverflow上面有个帖子Can I get polymorphic behavior without using virtual functions?专门讨论这个话题。里面我所考虑的这些方法都有。

最值得注意的当然是James McNellis列出的两个方案,第一种也是和类型绑定,用了union存储函数指针,并且将cast部分专门提了一个wrapper类出来,总体来说可读性和实用性也就那样(他提到不能用std::function,因为这东西也是用虚函数实现)。但第二种非常cooool,利用宏定义和函数重载,加上boost的特性模拟了一个vtable出来,至于型别转换、函数映射都隐藏得非常好,使用的类只要在定义中一两行代码就可以上路了。

One Response to “不用虚函数机制实现运行时多态”

  1. Payoneer中国 说道:

    这个网站是一个奇妙的网站。因此,我在这里提出一个建议:
    现在,在中国,你必须发送,并通过了世界上最好的汇款服务收钱的选项。注册,您将收到一个免费的借记卡,你可以从自动取款机直接拿钱。注册并免费获得25美元,第一次加油卡。中国Payoneer提供了超过200个国家,只有几个小时所支付的可能性。不要错过这个服务。

Leave a Reply