编码踩过的坑(虚接口)

本文介绍,由于虚函数的调用机制,导致的虚函数调用异常。

C++对外提供的接口,一般以接口类的public方法体现,如下:

class interface{
public:
    virtual int functionA();
    virtual int functionB();
    virtual int functionC();
};

如果在interface中新增一个方法(比如叫functionD),那么建议将新增的方法放在最后面。这样就算使用interface接口的代码没有重新编译,也不会影响到interface中老方法的使用。

想了解其中的原因为,必须先搞清楚虚函数的调用机制。

虚函数的调用,依赖虚表(vtbl)和虚表指针(vptrs)。每个包含(或继承)虚函数的类,都有一个虚表,虚表是个指向类中各个虚函数的指针数组(或链表);
包含(或继承)虚函数类的对象,都有一个隐藏的成员,这个成员指向就是虚表指针,它指向对应类的虚表。

以上面代码中的interface类为例。如果此类的虚表叫interface_vtbl,其内容应该为:
interface_vtbl[0] 指向 interface::functionA()
interface_vtbl[1] 指向 interface::functionB()
interface_vtbl[2] 指向 interface::functionC()

那么,虚函数的调用实际是这样的:

interface obj_i;
obj_i.functionB();
// 上面一行的伪代码为:
obj_i.vptrs[1](&obj_i); // &obj_i作为functionB()的this指针

回到增加一个新的方法functionD:

class interface{
public:
    virtual int functionA();
    virtual int functionD();
    virtual int functionB();
    virtual int functionC();
};

将functionD放在functionA的后面,而interface以头文件和目标文件的形式提供给下游模块。
如果下游模块没有重新编译,而是直接链接,想想下游模块调用functionB的时候,会出现什么情况。

实际上,调用functionB的时候,会走到functionD中。

这类情况,在多个模块协作编译链接的软件项目中,可能会出现。因此在实际中,建议将新的虚方法,放在最后。这样至少在不使用新方法的一些模块中,不会出现虚表指针跑飞的情况。

猜你喜欢

转载自blog.csdn.net/svp_Charles/article/details/78513393
今日推荐