将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
Add member variable to class in the shared library, will not break binary compatibility?
我想找一个例子来表达我对二进制兼容性的理解,但失败了。我想通过在开始或中间添加类成员来改变DLL中类成员的布局,并期望变量不能正确访问或者访问该变量将产生崩溃。然而,一切都很顺利。我发现,无论我如何将成员变量添加到类的任何位置,都不会出现崩溃,也不会破坏二进制兼容性。我的代码如下:
//untitled1_global.h
#include <QtCore/qglobal.h>
#if defined(UNTITLED1_LIBRARY)
# define UNTITLED1_EXPORT Q_DECL_EXPORT
#else
# define UNTITLED1_EXPORT Q_DECL_IMPORT
#endif
//base.h
class UNTITLED1_EXPORT Base
{
public:
Base();
double getA();
double getB();
private:
int arr[100]; //Add later to update the DLL
double a;
double b;
};
//derived.h
#include "dpbase.h"
class UNTITLED1_EXPORT Derived : public Base
{
public:
Derived();
void setC(double d);
double getC();
private:
char arrCh[100]; //Add later to update the DLL
double c;
};
下面是客户端代码,包含的base.h
、derived.h
与DLL中的不一样,一个是注释的,一个不是。实现和声明在DLL中是分开的。我试图直接访问变量,并通过函数访问变量(例如在main.cpp
开头注释(。
//main.cpp
#include "dpbase.h"
#include "dpbase2.h"
#include <QDebug>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Base base;
qDebug() << base.getA();
qDebug() << base.getB();
Derived base2;
base2.setC(50);
qDebug() << base2.getC();
return a.exec();
}
其中,类Base
、Derived
从dll导出。无论我如何在Base
或Derived
的任何位置添加成员变量,都不会出现崩溃,也不会破坏二进制兼容性。
我正在使用qt。有一个这里有同样的问题,但对我没有帮助。
此外,我删除了DLL中类的所有成员变量,我仍然通过链接DLL使用客户端中不存在的变量,赋值,获取……即使没有定义成员变量,在动态库中似乎也有足够的空间可以由客户端重新定义。太奇怪了!
我的问题是,为什么更改DLL中类成员的布局不会破坏二进制兼容性?并删除DLL中类的所有成员var,但为什么调用方仍然可以使用.h文件中的成员?
首先,您必须了解如何访问成员。
让我们采用
class UNTITLED1_EXPORT Base
{
public:
Base();
double getA();
double getB();
private:
double a;
double b;
};
当您访问a
时,就像执行*(this + 0)
一样。当您访问b
时,就像执行*(this + 8)
一样(假设double
是8个字节(。
然后当你这样改变你的等级时:
class UNTITLED1_EXPORT Base
{
public:
Base();
double getA();
double getB();
private:
int arr[100];
double a;
double b;
};
当您访问a
时,它将执行*(this + 400)
,当您访问b
时,它会执行*(this + 408)
。
现在,这可能是一个问题,也可能不是一个问题。如果您仅通过getA()
和getB()
访问a
和b
,并且它们是在DLL中的.cpp中定义的,那么您将在更新类的同时更新getter的定义。
您可以通过内联getter的定义来创建一些奇怪的行为:
class UNTITLED1_EXPORT Base
{
public:
Base();
double getA() { return a; }
double getB();
private:
double a;
double b;
};
在这种情况下,.exe可能有自己的getA()
和getB()
副本。这意味着,在您更新DLL并添加int arr[400]
后,您的.exe仍将尝试访问现在被arr
占用的*(this + 0)
。
这是未定义的行为,但它不会使您的程序崩溃,因为您正在访问分配的内存。
如果你反其道而行之:
- 使用
arr
编译您的exe - 删除
arr
- 生成DLL
- 运行.exe
那么你更有可能发生车祸。因为exe将尝试访问this + 400
,而Base
只有16个字节。但由于多种原因,它仍然不能保证崩溃。例如,this + 400
可能是有效的。但更重要的是,这取决于从哪里为Base
分配内存。如果你在.exe中执行new Base
,那么即使在你更改了DLL之后,它也会分配416个字节。但是,如果在DLL中执行new Base
,它将只分配16个字节。
这是一个例子。
这是.exe:的标题
class Base
{
public:
Base();
double getA() const { return a; }
double getB() const { return b; }
static Base *create();
void print();
private:
double a;
double b;
};
DLL 的标头
类基础{公共:Base((;double getA((const{return a;}double getB((const;static Base*create((;void print((;私人:int arr【400】双a;双b;};
DLL的代码:
Base::Base()
{
for (int i = 0 ; i < 100 ; ++i) {
arr[i] = i;
}
a = 3.14;
b = 1.42;
}
double Base::getB() const { return b; }
Base *Base::create()
{
return new Base();
}
void Base::print()
{
std::cout << a << std::endl;
std::cout << b << std::endl;
}
我的exe中的代码是:
Base *b = Base::create();
std::cout << b->getA() << std::endl;
std::cout << b->getB() << std::endl;
b->print();
通常我应该期望输出:
3.14
1.42
3.14
1.42
但在实践中,我有:
2.122e-314
1.42
3.14
1.42
原因是,对于第一行(2.122e-314
(,exe正在地址this + 0
处查找a
,但由于该内存现在被arr
占用,因此与我们所做的一样:std::cout << *((double *)arr)
。b
的值不受影响,因为b
总是从DLL中读取的,而getB()
不是内联的。
现在,如果我用Base *b = new B();
更改Base *b = Base::create();
,程序就会崩溃,因为new
是从.exe中完成的,它将只分配16个字节,而Base()
将访问416个字节中的所有成员变量。
备注
为了这个答案的目的,我做了指针算术,假设增量总是1。事实并非如此。
所以当我写this + 8
时,它在C++中被理解为reinterpret_cast<double *>(reinterpret_cast<char *>(this) + 8)
。
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 添加noexcept是否会破坏二进制兼容性
- 在Qt中使用C++Lambda函数作为插槽是否有助于保持库的二进制兼容性?
- 为什么新的虚拟函数不会破坏二进制兼容性的现象?
- 在类中添加新的成员变量会影响二进制兼容性
- 2015年视觉工作室的二进制兼容性
- 保证C++库二进制兼容性的简单方法,C链接
- 添加move构造函数会破坏二进制兼容性吗
- 关于 Linux 的二进制兼容性
- 我们什么时候打破二进制兼容性
- Linux二进制兼容性
- 可以添加静态变量破坏 Linux 上的二进制兼容性
- 使用按引用传递而不是按指针传递时的二进制兼容性
- 旧发行版上的二进制兼容性(使用C++11)
- 在C++中更改参数名称时的二进制兼容性
- 不同类中相同结构定义的二进制兼容性
- 内联如何限制升级版本的二进制兼容性
- VS2010和VS2012之间的二进制C++库兼容性
- 由于虚拟函数,二进制兼容性中断
- 更改方法以添加隐藏的this指针是否会破坏二进制兼容性