c ++类实例内存布局再一次

时间:2012-05-25 22:57:09

标签: c++ memory layout shared-libraries

我知道之前已经问过这个问题,但在你给我一个减号并报告重复的问题之前,请考虑一下这个问题:

在之前的所有答案中,每个人都说对象内存布局依赖于编译器。那怎么样,共享库(* .dll,* .so)可以导出和导入c ++类,即使来自不同的编译器,它们肯定可以组合在一起?考虑一下在mingw下编写的DirectX应用程序。 DirectX是使用MSVC ++编译的,那么这些环境如何在内存布局上达成一致呢?我知道DirectX在很大程度上依赖于C ++类和多态。

要求不同:让我们说我有一个选择的架构(例如Windows,intel x86),我正在尝试编写一个新的编译器。我如何知道如何访问由另一个编译器编译的.dll lib提供的类实例(vtable,成员字段)?或者就是这样:M $编写了VC ++,从那以后它就是不成文的标准,而且出于兼容性原因,其他每个编译器也都这样做了吗?那么linux或其他操作系统呢?

编辑:

好的我承认,由于COM规范,DirectX的示例很糟糕......

另一个例子:QT。我正在使用QT和mingw,但我知道也有可用的MSVC版本。我不知道差异是仅在标题中,还是共享库(dll-s)也不同。如果它们是,那是否意味着我必须使用包含qt库的应用程序来分发我的应用程序,所以如果有人碰巧有一个用于不同编译器的应用程序,它将不会混淆? (好的记忆和代码共享,对吗?)。或者他们是一样的,还有一些不成文的法律呢?

EDIT2:

我安装了一个不同的qt版本(msvc 2010),只是为了查看共享内容和未共享内容。似乎共享(他们真的共享)库是不同的。似乎我真的必须在我的应用程序中提供qt-libs然后......这不是一件小事(例如QtGui 8-9MB)。那些其他较小的库,其作者不是那么善于为其他编译器提供版本呢?这是否意味着我被他们的原始编译器困住了?如果我想使用由不同编译器编译的两个不同的库,该怎么办?

2 个答案:

答案 0 :(得分:8)

基本上你问的是ABI。

在某些情况下(例如,在Itanium上),有一个指定ABI的文档,基本上每个人都遵循该文档。

在DirectX的情况下,它有点相同:Microsoft已经发布了COM的规范,因此任何遵循这些规范的人都可以与任何COM对象进行互操作(在合理范围内 - 64位编译器可能不是使用为Windows 3.1编写的16位COM对象。

对于大多数其他事情,你或多或少都可以自己解决问题。在文档的方式中通常至少会发布一些内容,但至少根据我的经验,它经常会略过一些最终重要的细节,而不是完全可靠地更新,并且在某些情况下是完全错误的。在大多数情况下,它的组织也很差,因此您可以从任何可以找到它的地方收集到的东西,当您用完信息(或耐心)时,需要进行一些逆向工程来填补缺失的部分。< / p>

编辑:对于像Qt库这样的非COM事物,你有几个选择。一种是静态链接到Qt库,因此您需要的Qt代码直接链接到您的可执行文件。如果你想使用一个DLL,那么是的,你漂亮很难与你的应用程序分发一个,这将特定于你用来生成应用程序本身的编译器。不同的编译器(或者甚至是具有相同编译器的不同版本或编译标志集)通常需要不同的DLL(可能是相同的源代码,但是为了适应编译器的更改而构建)。

这有例外,但它们就像我上面概述的那样。例如,用于Windows的英特尔编译器通常使用Microsoft的标准库,并且可以使用为Microsoft编译器构建的大多数(如果不是全部)其他库。这几乎是因为英特尔已经向后倾斜,以确保他们的编译器使用相同的调用约定,名称修改方案等,就像微软一样。它之所以有效,是因为他们付出了很多努力才能使其发挥作用,而不是因为任何不成文的法律,或类似的事情。

答案 1 :(得分:2)

DirectX是一种基于COM的技术,这就是为什么它可以在C中与各种编译器一起使用。在引擎盖下COM接口是一个类似C的结构,模拟VMT。

从技术上讲,DirectX有一个midl自动生成的C ++接口,但是一般断言可以使用.dll中跨不同编译器导出的类是错误的。

EDIT1:

使用MSVC构建的QT dll与gcc构建的QT dll不兼容,不幸的是,由于自然原因:不同的C ++编译器 - 不同的ABI和不同的运行时库(mingw使用较旧的MSVCRT,这就是纯粹的原因) -C.dll可以在MSVC中使用,反之亦然)。有些编译器偶然或部分故意匹配他们的ABI,但绝对不是MSVC / gcc的情况。 QT也可以构建为静态库,因此重新分配可能只是静态链接的东西。

DLL中C ++类的名称错误很大程度上取决于使用的编译器前端。许多知名公司的商业编译器都使用EDG的C ++解析器,这就是为了重命名函数的类名具有相似或匹配的签名。

EDIT2:

“如果我想使用由不同编译器编译的两个不同的库,该怎么办?”

如果您迫切需要两个库中的某些特定功能(我的意思是一些具体的操作,而不是整个框架),那么没有库的源代码的方法是编写一些包装器并将此包装器编译为C-风格.dll。

将此视为“两个不同的C ++ - es,C ++ - 1和C ++ - 2”。 ABI / Runtime的问题与使用C语言中的Pascal代码或链接到一些较旧的Fortran库没有区别。