C++继承和动态库

问题描述:

这个想法如下。我有一个库版本1类(如下所示):C++继承和动态库

class MY_EXPORT MyClass 
{ 
public: 
    virtual void myMethod(int p1); 
} 

在第2版,类进行了修改,这一点:

class MY_EXPORT MyClass 
{ 
public: 
    virtual void myMethod(int p1); 
    virtual void myMethod2(int p1, int p2); 
} 

//implementation of myMethod2 in cpp file 
void MyClass::myMethod2(int p1, int p2) 
{ 
    myMethod(p1); 
    //... 
} 

现在想象编译反对票库第1版用户,并通过覆盖myMethod扩展MyClass。现在他将库更新为版本2,而不用重新编译。让我们进一步假设动态链接器仍能成功找到该库并加载它。

的问题是,如果我调用该方法instance->myMethod2(1, 2); somwhere图书馆内,将它的工作,或将应用程序崩溃?在这两种情况下,班级都没有成员,因此规模相同。

+0

由于继承类并不知道关于改变的接口,这将有大约在其虚拟表中没有的信息。这将导致*未定义的行为*。简而言之:如果接口发生变化,则应重新编译依赖于这些接口的应用程序。 –

我不认为有一点猜测,如果该应用程序会崩溃与否,行为是不确定的。应用程序必须重新编译,因为库中存在ABI更改。

当库调用instance->myMethod2(1, 2);时,它必须通过在应用程序代码中创建的虚拟表,假设只有一个虚拟方法:myMethod。从这一点,你会得到未定义的行为。总之,当库ABI改变时,你必须重新编译你的应用程序。

+1

崩溃实际上是首选,因为那时没有歧义,你知道你有未定义的行为。 –

+0

@MarkRansom是的,它被提供,但可能会有一种情况,它默默地工作,并做错了事情,甚至完全是预期的事情。 – Pavel

KDE C++ ABI guidelines明确禁止这样的改变。派生类的虚拟表不包含新方法的地址,因此在派生类的对象上虚拟调用这些方法将会崩溃。

通过改变类的定义而无需重新编译,你已经违反了One Definition Rule。没有重新编译的用户正在使用旧的定义,而您的库正在使用新的定义。这导致未定义的行为。

要看到这是如何体现,考虑典型实现的虚函数,它使用一个虚函数表派遣函数调用。库用户已经派生了一个类,并且这个派生类在VTable中只有一个函数。如果指向该类的指针或引用被传递到库中,并且库试图调用第二个函数,它将尝试访问不存在的VTable条目。这几乎总是会导致崩溃,但对于未定义的行为,没有任何保证。