C++ 继承和动态库
C++ Inheritance and dynamic libraries
思路如下。我有一个带有 class 的库版本 1,如下所示:
class MY_EXPORT MyClass
{
public:
virtual void myMethod(int p1);
}
在版本 2 中,class 被修改为:
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);
,它会起作用,还是应用程序会崩溃?在这两种情况下,class 没有成员,因此大小相同。
我认为猜测该应用程序是否会崩溃没有意义,行为未定义。必须重新编译应用程序,因为库中有 ABI 更改。
当库调用 instance->myMethod2(1, 2);
时,它必须通过在应用程序代码中创建的虚拟 table,假设只有一个虚拟方法:myMethod
。从那时起,您将获得未定义的行为。简而言之,当库 ABI 更改时,您必须重新编译应用程序。
KDE C++ ABI guidelines明确禁止这样的改变。派生 类 的虚拟表将不包含新方法的地址,因此对派生 类 的对象的这些方法的虚拟调用将崩溃。
通过在不重新编译的情况下更改 class 的定义,您违反了 One Definition Rule。没有重新编译的用户使用的是旧定义,而您的库使用的是新定义。这会导致未定义的行为。
要了解这可能如何表现,请考虑使用 VTable 调度函数调用的虚拟函数的典型实现。库用户派生了一个class,这个派生的class在VTable中只有一个函数。如果指向此 class 的指针或引用被传递到库中,并且库尝试调用第二个函数,它将尝试访问不存在的 VTable 条目。这几乎总是会导致崩溃,尽管在涉及未定义行为时无法保证。
思路如下。我有一个带有 class 的库版本 1,如下所示:
class MY_EXPORT MyClass
{
public:
virtual void myMethod(int p1);
}
在版本 2 中,class 被修改为:
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);
,它会起作用,还是应用程序会崩溃?在这两种情况下,class 没有成员,因此大小相同。
我认为猜测该应用程序是否会崩溃没有意义,行为未定义。必须重新编译应用程序,因为库中有 ABI 更改。
当库调用 instance->myMethod2(1, 2);
时,它必须通过在应用程序代码中创建的虚拟 table,假设只有一个虚拟方法:myMethod
。从那时起,您将获得未定义的行为。简而言之,当库 ABI 更改时,您必须重新编译应用程序。
KDE C++ ABI guidelines明确禁止这样的改变。派生 类 的虚拟表将不包含新方法的地址,因此对派生 类 的对象的这些方法的虚拟调用将崩溃。
通过在不重新编译的情况下更改 class 的定义,您违反了 One Definition Rule。没有重新编译的用户使用的是旧定义,而您的库使用的是新定义。这会导致未定义的行为。
要了解这可能如何表现,请考虑使用 VTable 调度函数调用的虚拟函数的典型实现。库用户派生了一个class,这个派生的class在VTable中只有一个函数。如果指向此 class 的指针或引用被传递到库中,并且库尝试调用第二个函数,它将尝试访问不存在的 VTable 条目。这几乎总是会导致崩溃,尽管在涉及未定义行为时无法保证。