二级制兼容

二进制兼容ABI(application binary interface)主要指动态库文件单独升级,现有用到老动态库的应用程序是否受到影响。

在linux系统升级动态库文件可能会出现这种情况

下面介绍一下什么是二进制兼容,又为什么会出现这种情况

二进制兼容:

1 升级库文件,不影响使用库文件的程序。(说明)

2 新库必然有新头文件,但是旧的二进制可执行文件还是按照旧的头文件中的“使用说明”来调用库。(原因)

会出现二进制兼容的几个具体情况

1.类普通成员函数 void function(int) 改成了 void function(flout) 。之前的EXE会传int进来,新库会用double的长度取数据。从而发生undefined symbol

2 基类增加虚函数会导致基类虚表发生变化。老EXE调用虚表的时候给出的slot是老的,但是新库里面的这个slot已经是另一个函数了。

3 给函数增加默认参数

4 增加默认模板类型

5 改变enum的值

6 给class Bar增加数据成员导致sizeof(Bar)的值变大

7 如果EXE里调用new Bar,导致new出来的内存盛不下新的Bar对象(构造函数会使用新DLL中的构造函数来填充数据),从而:

1)如果新的库实现访问了新的数据成员肯定会访问到一个无法预知的地方;

2)如果EXE得到的是shared_ptr 由DLL来管理内存,那么此时是安全的。

3)如果EXE调用的是p->member 那么肯定不对,因为偏移量可能因为member前面插入了新的成员而被新DLL中构造函数填充了新的成员,从而访问的并不是老的member。

4)如果EXE是使用p->get_member()来获取数据,那么是正常的。

5) 如果p->get_member()是inline的,那么是不安全的,因为偏移量已经在EXE中了。

8 虚函数做接口的基本上都是二进制不兼容的。

二进制安全

1 增加新的class(定义在新DLL中,老的EXE里没有)

2 增加非virtual函数(定义在新DLL中,老的EXE里没有)

3 增加static成员函数(定义在新DLL中,老的EXE里没有)

解决办法之pimpl技法:

1 头文件只暴露非virtual函数,class的大小固定为sizeof(Impl*)

//头文件
classGraphics
{
public:
    Graphics();
    ~Graphics();//不能是虚函数
voidf1(void);
voidf2(int);
voidf3(int, int);//增加成员函数可以直接添加非virtual函数,不影响二进制兼容
private:
classImpl;//头文件只放声明,sizeof(Graphics)不会变化,成员变量的扩充在Impl中添加
    boost::scoped_ptr m_impl;//sizeof(Graphics) == sizeof(Graphics::Impl*)
}
//库的源文件
Graphics::Graphics()
    :m_imp(new Impl)
{
}
Graphics::~Graphics()
{
}
voidGraphics::f1(void)
{
    m_impl->f1();//调用转发
}