使用placementnew在构造函数中调用构造函数

Using placement new to call constructor within constructor

本文关键字:构造函数 调用 placementnew 使用      更新时间:2023-10-16

这些天一直在挣扎。

问题出在构造函数调用上。

我写了一段代码,比如:

#include <iostream>
using namespace std;
class Foo
{
  private: int _n;
  public:
  Foo() { Foo(5);}
  Foo(int n) {_n=n; cout << n << endl; }
};
int main()
{
   Foo* foo = new Foo();
   return 0;
}

当我在外部使用默认构造函数构建Foo对象时:

Foo* f = new Foo();

我假设变量_n是5,但它不是。

这在Java中可以,但在c++中不行。

此外,在Visual C++6 sp 6中,

Foo() {this->Foo(5);}

工作。

然而,这个表达式被gcc/g++4拒绝了。

最后,我找到了解决办法。

只需将默认构造函数更改为

Foo() {Foo(5);}

进入

Foo() { new (this) Foo(5); }

解决了问题。

括号中的"this"做什么?

(this)所做的是在this指向的位置创建一个全新的Foo对象(这称为放置新对象)。您应该只在charunsigned char的数组中使用它,而不在其他数组中使用(而且几乎从不在其他数组)。由于您正在this已经开始构建的位置构建Foo,因此您所做的是未定义的行为,并且如果您有基类,则会到处泄漏资源。从历史上看,通常的做法只是将初始化转移到一个私有函数。

class Foo {
public:    
  Foo() { init(5);}    
  Foo(int n) {init(n);}
private: 
  int _n;
  void init(int n) {
    _n=n;
  };
}

在C++11中,构造函数应该能够用这种语法相互调用,但我还不知道哪些编译器支持这种语法。根据Apache的说法,它得到了GCC 4.7和Clang 3.0的支持,但还没有得到英特尔C++和VC++的支持。

class Foo {
public:    
  Foo() : Foo(5) {}
  Foo(int n) {_n=n;}
private: 
  int _n;
}

Foo() { Foo(5);}开始的代码开始构造this,然后在堆栈上用参数5创建一个全新的Foo对象,然后销毁它,然后认为自己完全构造好了,而没有初始化它自己的任何值。这就是为什么它编译并运行,但似乎什么都没做。

在C++11中,您可以使用委托构造函数来指定它

Foo() : Foo(5) { }

括号中的(this)表示运算符将使用this的地址作为初始化类的地址。

这是非常危险的:您在当前对象的构造函数中,并且在同一内存空间上调用一个新的构造函数。想象一下,如果你从另一个类继承,会发生什么!

至于您的问题,不能从构造函数中调用另一个重载构造函数。典型的解决方案是有一个初始化类的方法:

class Foo
{
  int _n;
public:
  Foo() { init(5); }
  Foo( int i) { init(i); }
  void init(int i) { _n = i; }
};

我在这里遇到了同样的问题:又一次C++对象初始化询问;请随意查看解决方案。

Foo() { new (this) Foo(5); }

是一个"placement-new"运算符,它在预先分配的内存上调用构造函数。

现在,对于你的另一个问题——C++11完全允许这样做(从另一个调用构造函数),但早期的标准(尤其是MSVC 6使用的标准)没有,所以使用那些丑陋的init()方法是你的选择。

C++中的正确语法是

class Foo { 
  private: int _n; 
  public: 
  Foo() : Foo(5) {} 
  Foo(int n) _n(n) {} // As suggested by another member's edit, initializer lists are preferred
};

或者,C++允许默认参数值,例如

Foo(int n = 5);

这允许您编写一个构造函数,而不是两个构造函数。

此外,您应该了解内联函数和非内联函数之间的区别。这种Java风格的编程是有效的C++,但它有优缺点,还有至少一种其他选择。