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

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

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

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

最最简单的思路:

(static_cast(d))->Foo();

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

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

class Base
{
    public:
    void Foo()
    {
        if ( _pVFunc == NULL )
            cout<<"Base::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"<

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

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

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