在构造函数中使用placement new调用构造函数

4

最近我遇到了一些困难。

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

我写了一个代码片段,像这样:

#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”是什么作用?


1
这个问题在谷歌...咳咳...C++ FAQ中有详细解答:"一个类的构造函数能否调用同一类的另一个构造函数来初始化this对象?" - Sean Bright
2
在Java中没问题,但在C++中不行。不要试图根据你从Java中了解到的知识来理解C++。这不仅没有帮助,而且实际上是有害的。 - R. Martinho Fernandes
4
如果我没记错的话,解决这个问题的一种方法是不声明Foo()函数,而是将Foo(int n)函数的声明改为Foo(int n=5)。这样,该构造函数就可以被用作默认值。由于我已经有一段时间没有做任何C++编程了,所以我的理解可能不正确。 - Dan F
https://dev59.com/G2s05IYBdhLWcg3wQvse - user195488
2
只是澄清一下(不回答你的问题):在你的第一个例子中,错误的代码Foo() { Foo(5);}构造了一个类型为Foo的对象,并将参数设置为5,不做任何工作,然后立即销毁它。 - anatolyg
5个回答

4
代码中的 (this) 所做的是在被 this 指向的位置创建一个全新的 Foo 对象(这称为定位 new)。你只应该在 charunsigned char 数组中使用它,而不是在其他任何地方使用(即使在数组中也几乎不需要使用)。因为你正在构造一个 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支持,但尚未得到Intel C++或VC++的支持。

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

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

大家都忽略了他的代码有一个错误。他正在调用Foo(5)的构造函数,但是临时对象在块的末尾被销毁了。 - user195488
@user195488:不,临时对象会在分号处被销毁。 - jdh8

3
在C++11中,您可以通过使用委托构造函数来指定这个:
Foo() : Foo(5) { }

1

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

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

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

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

我在这里遇到了相同的问题:另一个C++对象初始化疑问;随意查看解决方案。


0

在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++中是有效的,但它有其优缺点以及至少另一种替代方案。


2
请注意,这仅适用于C++11。 - Xeo
@Xeo 这段时间我已经很久没有认真地使用C++进行编程了。 我相当确定在C++11之前我能够从初始化列表中调用其他构造函数。也许我的记忆有误。 耸肩 - Code-Apprentice

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

是一种“放置新”运算符,它在预先分配的内存上调用构造函数。

现在,回答你的另一个问题 - C++11允许这样做(从一个构造函数中调用另一个构造函数),但早期标准(特别是MSVC 6使用的标准)不支持,因此使用那些丑陋的init()方法是你的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接