我对一个对象的不同版本,它们的大小和分配有疑问。该平台是Solaris 8(及更高版本)。
假设我们有程序A,B和C都链接到共享库D.某些类在库D中定义,我们称之为'classD',并假设大小为100字节。现在,我们想为classD添加一些成员用于下一版本的程序A,而不会影响现有的二进制文件B或C.新的大小将是120字节。我们希望程序A使用classD的新定义(120字节),而程序B和C继续使用classD的旧定义(100字节)。 A,B和C都使用运算符“new”来创建D的实例。
问题是,运营商“new”何时知道要分配的内存量?编译时间或运行时间?我担心的一件事是,程序B和C期望classD为100字节,而新的共享库D需要120字节用于classD,如果我将它们与程序B和C链接,这种不一致可能会导致程序B和C中的内存损坏换句话说,新classD所需的额外20个字节的区域可以通过程序B和C分配给其他一些变量。这个假设是正确的吗?
感谢您的帮助。
答案 0 :(得分:6)
更改类的大小是二进制不兼容。这意味着如果更改classD
的大小而不重新编译使用它的代码,则会得到未定义的行为(很可能是崩溃)。
解决此限制的一个常见技巧是设计classD
,以便可以以二进制兼容的方式安全地扩展它,例如使用Pimpl习语。
在任何情况下,如果您希望不同的程序使用不同版本的类,我认为您别无选择,只能发布共享库的多个版本,并将这些程序链接到相应的版本。
答案 1 :(得分:3)
编译时间,您不应更改其客户端下的共享对象大小。
有一个简单的解决方法:
class foo
{
public:
// make sure this is not inlined
static foo* Create()
{
return new foo();
}
}
// at the client
foo* f = foo::Create();
答案 2 :(得分:2)
你是正确的,在编译时定义了内存大小,应用程序B / C将面临严重的内存损坏问题。
无法在语言级别明确处理此问题。您需要使用操作系统来为应用程序获取适当的共享库。
您需要对库进行版本化。
由于使用构建工具没有明确的方法,您需要使用文件名来完成此操作。如果你看一下大多数产品,这大概就是它们的工作原理。
在lib目录中:
libD.1.00.so
libD.1.so -> libD.1.00.so // Symbolic link
libD.so -> libD.1.so // Symbolic link
现在在编译时指定-lD并且它链接到libD.1.00.so,因为它遵循符号链接。在运行时,它知道使用此版本,因为这是它编译的版本。
所以你现在将lib D更新为2.0版
在lib目录中:
libD.1.00.so
libD.2.00.so
libD.1.so -> libD.1.00.so // Symbolic link
libD.2.so -> libD.2.00.so // Symbolic link
libD.so -> libD.2.so // Symbolic link
现在,当您使用-libD进行构建时,它会链接到版本2.因此,您重新构建A,它将从现在开始使用lib的第2版; B和C仍将使用版本1.如果重建B或C,它将使用新版本的库,除非在构建-libD.1时明确使用旧版本的库
有些链接器不知道如何很好地遵循符号链接,因此有链接器命令可以提供帮助。 gcc使用'-install_name'标志,你的链接器可能有一个稍微不同的命名标志。
作为运行时检查,将版本信息放入共享对象(全局变量/函数调用等)通常是个好主意。因此,在运行时,您可以检索共享库版本信息并检查您的应用程序是否兼容。如果没有,您应该退出相应的错误消息。
另请注意:如果将D的对象序列化为文件。您需要确保维护有关D的版本信息。 Libd.2可能知道如何读取版本1 D对象(有一些明确的工作),但反之则不然。
答案 3 :(得分:1)
内存分配在编译时计算出来。在D中更改类的大小将触发重新编译。
如果适用,请考虑公开从相关类派生以扩展它。或者,将其组合在另一个对象中。
答案 4 :(得分:1)
要分配的内存量是在编译时执行的,例如
new Object();
但它可以是动态参数,例如
new unsigned char[variable];
我真的建议你通过一些中间件来实现你想要的。 C ++不保证二进制接口。
你看过protobuf吗?
答案 5 :(得分:0)
除了上面提到的'ad hoc'技术之外,你还可以通过说你的新类A真的是'旧'类A的子类来模拟系统的兼容性。这样,你的旧代码继续工作,但是需要修改所有需要扩展功能的代码。
这个设计原则在COM世界中清晰可见,其中特别是接口永远不会在版本上进行更改,只能通过继承进行扩展。接下来,它们只用CreateInstance
方法构造类,它将分配问题移到包含类的库中。