Would unused private virtual methods allow future expansion without breaking ABI compatibility?

吃可爱长大的小学妹 提交于 2019-12-03 16:49:56

Since the function reserved1 is only there to preserve vtable layout compatibility, presumably nothing in the client code will call it.

If it isn't called client code doesn't need any linker reference to it: this is obviously all platform-specific, but in general your scheme should work fine.

Are the virtual methods really private though? If they can't be called or overridden from the client, you could just expose an opaque forward declaration and keep the implementation entirely inside your dynamic lib (eg, MyClass::PImpl).

You don't need to do this because, as long as the order of the methods is not altered, you can add methods to the end of the vtable without changing the beginning; pointers are accessed in the vtable by their offset from the beginning, so adding something to the end won't affect anything.

This is the entire point of interface classes like this: you can pass pointers to derived classes, which have a vtable with extra methods on the end, to functions which expect a pointer to a base class.

For instance:

// Class:

class MyClass {
public:
    //public interface

private:

    virtual void foo1(int);
    virtual void foo2(int, bool);
    virtual void foo3(double);

    class Impl;
    Impl* impl_;
};

// current vtable:

+-------------------+
| foo1(int)         | < offset 0
| foo2(int, bool)   | < offset 1
| foo3(double)      | < offset 2
+-------------------+

// code is compiled and references offsets 1 and 2 in the vtable

// then you change the class with an added method:

class MyClass {
public:
    //public interface

private:

    virtual void foo1(int);
    virtual void foo2(int, bool);
    virtual void foo3(double);
    virtual void foo4(int, int);

    class Impl;
    Impl* impl_;
};

// New vtable:

+-------------------+
| foo1(int)         | < offset 0
| foo2(int, bool)   | < offset 1
| foo3(double)      | < offset 2
| foo4(int, int)    | < offset 3
+-------------------+

// the offsets of the first three are the same so the old code
// that was compiled to use offsets 1 and 2 still works

You can use reserved virtual functions at end of class declaration, but your class should has an exported constructor or a factory method that can return a pointer to created class from the shared library side:

    class MyClass {
    public:
        MyClass(...);
        ...
    };

OR

    class MyClass {
    public:
        MyClass* create(...);
        ...
    };

Your sample class MyClass has no any declared constructor, so the compiler (GCC) will generate inline constructor for this class automatically. This constructor will create old v-table for old client applications, which has no entry for foo4.

If you declare a factory method then it will return pointer to a class object with new v-table for old clients, so that they will be able to find foo4 and run your new code.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!