C ++运算符new,对象版本和分配大小

时间:2009-07-06 16:43:19

标签: c++ memory-management new-operator

我对一个对象的不同版本,它们的大小和分配有疑问。该平台是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分配给其他一些变量。这个假设是正确的吗?

感谢您的帮助。

6 个答案:

答案 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方法构造类,它将分配问题移到包含类的库中。