考虑以下类
// in library
class A {
public:
static A* instance(){
static A self;
return &self;
}
void foo() { }
private:
A () {}
int a{0};
};
在动态库中定义
然后定义一个带有额外字段b
的新版本的类。
class A {
// as before
private:
int b{0};
};
新版本的库是否违反了ABI ?
对于一个普通的类,我会说毫无疑问是,但对于一个单例,我认为它/不是/一个ABI中断(应用程序只处理指向A
的指针,没有改变,没有公共改变)。但我不确定,我想要一个明确的答案。
如果应用程序代码需要一个完整类型来编译A
,那么是的,这将是一个ABI中断。
如果应用程序仅仅存储一个指向A
的指针(并调用库来获取A*
),那么就没有ABI中断。
可以与c的FILE*
指针进行比较。
A
是单例的事实是转移注意力。
是的。
首先,关于标准的保证,更改类定义中的任何单个令牌都需要所有翻译单元(包括库)使用新定义,以不破坏ODR并导致未定义行为。
显然,在实践中存在一些情况,您可以依靠特定于实现的行为来使其工作,而不会在实际意义上破坏ABI。
在您正在显示的情况下,至少instance
函数将是一个问题。这个函数是一个内联函数,必须知道A
的确切布局。但是,由于它是一个内联函数,因此您无法控制库和库用户使用该函数的相同版本。你可以很容易地得到一个不匹配,然后对象可能被构造成错误的布局。
即使你让instance
不是inline
,你也会有同样的问题,构造函数也是inline
。