7

这个问题中,讨论了在编译器不支持new和放置new时创建工厂方法。显然,如果通过放置new完成的所有必要步骤都以某种方式重现,则可以使用 malloc() 制作一些合适的解决方案。

Placement new有什么作用——我会尽量列出并希望不要错过任何东西——除了以下内容?

  • 递归调用所有基类的构造函数
  • 为所有成员变量调用构造函数和初始化程序(如果有)
  • 相应地设置 vtable 指针。

还有哪些其他动作?

4

3 回答 3

6

除了分配内存外,布局new可以做任何常规的事情。new

我认为您基本上已经确定了发生的事情,并进行了一些小的澄清:

  • 显然类本身的构造函数也被调用
  • vtable 指针作为构造函数调用的一部分被初始化,而不是单独初始化。这意味着一个部分构造的对象(认为构造函数中抛出的异常)将其 vtable 设置为构造进行到的点。

构造/初始化的顺序如下:

  1. 声明顺序的虚拟基类
  2. 声明顺序中的非虚拟基类
  3. 声明顺序中的类成员
  4. 类构造函数本身
于 2009-06-23T09:49:00.447 回答
4

相应地设置 vtable 指针

这部分几乎完全是实现定义的。您的编译器可能不使用 vtables。可能有多个 vtable 指针,或者一个或多个指向非 vtable 事物的指针。多重继承总是很有趣,虚拟基类也是如此。不保证此元数据可复制memcpy到另一个对象,因此指针不必是绝对的。那里可能存在相对于对象指针本身的偏移量。

IIRC通常发生的是调用基类构造函数,然后将vtable指针设置为基类,然后调用第一个派生类构造函数,等等。这是为了满足规范中关于何时发生的要求在构造函数中调用虚函数。据我记得,标准中没有“动作列表”,只有定义的初始化顺序。

因此,不可能概括实现的功能,特别是因为您拥有的不是 C++ 标准的实现。如果它通过省略“新”来偷工减料,大概有充分的理由,因为它认为你不应该在目标平台上使用它,那么谁知道它忽略了语言的其他规则。如果可以使用 malloc 和一些指针推送来模拟“new”,那么究竟为什么编译器不只是实现 new 呢?我认为您需要提出带有特定编译器和平台标记的问题,以便您编译器的任何专家都能做出回应。

于 2009-06-23T09:56:23.910 回答
0

@laalto 的回答通常是正确的。然而,一个例外证明了这一规则。

vtable 指针作为构造函数调用的一部分被初始化,而不是单独初始化。

Microsoft C++ 编译器知道所谓的本地 vftable,即 vftable 对 DLL 来说是本地的,并且将为每个导入的类克隆。这是因为编译器想要提供一个修改过的析构函数来包装原始析构函数(这里是 sa )。

每当您构造导入类的对象时,编译器都会生成代码,该代码会在调用构造函数后用本地指针覆盖原始 vftable 指针。此代码也出现在放置新调用中。

除了上面帖子中提到的那些之外,还有另一种解决方法。此解决方法不会强制您更改原始头文件。请看这里:https ://godbolt.org/g/YQsffY

于 2017-04-18T09:07:52.830 回答