类型名称后的括号是否与new有区别?

时间:2009-03-06 19:39:13

标签: c++ constructor initialization new-operator c++-faq

如果'Test'是普通类,那么:

之间是否有任何区别?
Test* test = new Test;

Test* test = new Test();

6 个答案:

答案 0 :(得分:906)

让我们变得迂腐,因为存在实际上会影响代码行为的差异。以下大部分内容取自对"Old New Thing" article的评论。

有时新运算符返回的内存将被初始化,有时它不会取决于你正在新建的类型是POD (plain old data),还是它是一个包含POD成员的类,并且使用编译器生成的默认构造函数。

  • 在C ++ 1998中,有两种类型的初始化:零和默认
  • 在C ++ 2003第三种初始化中,添加了值初始化。

假设:

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

在C ++ 98编译器中,应该发生以下情况:

  • new A - 不确定的价值
  • new A() - 零初始化

  • new B - 默认构造(B :: m未初始化)

  • new B() - 默认构造(B :: m未初始化)

  • new C - 默认构造(C :: m为零初始化)

  • new C() - 默认构造(C :: m为零初始化)

在符合C ++ 03的编译器中,事情应该如下:

  • new A - 不确定的价值
  • new A() - value-initialize A,这是零初始化,因为它是POD。

  • new B - 默认初始化(离开B :: m未初始化)

  • new B() - 值初始化B,它对所有字段进行零初始化,因为它的默认ctor是编译器生成的而不是用户定义的。

  • new C - 默认初始化C,调用默认的ctor。

  • new C() - value-initializes C,调用默认的ctor。

因此,在所有版本的C ++中,new Anew A()之间存在差异,因为A是POD。

对于案例new B(),C ++ 98和C ++ 03之间的行为存在差异。

这是C ++尘封的角落之一,可以让你发疯。在构造一个物体时,有时候你需要/需要它们,有时候你绝对不能拥有它们,有时它们并不重要。

答案 1 :(得分:56)

new Thing();明确表示你想要一个被调用的构造函数,而new Thing;被认为意味着你不介意是否没有调用构造函数。

如果在具有用户定义构造函数的struct / class上使用,则没有区别。如果调用一个简单的结构/类(例如struct Thing { int i; };),那么new Thing;就像malloc(sizeof(Thing));new Thing();就像calloc(sizeof(Thing)); - 它初始化为零。

陷阱介于两者之间:

struct Thingy {
  ~Thingy(); // No-longer a trivial class
  virtual WaxOn();
  int i;
};

在这种情况下,new Thingy; vs new Thingy();的行为在C ++ 98和C ++ 2003之间发生了变化。请参阅Michael Burr关于如何以及为什么的解释。

答案 2 :(得分:19)

不,他们是一样的。但是:

之间存在差异
Test t;      // create a Test called t

Test t();   // declare a function called t which returns a Test

这是因为基本的C ++(和C)规则:如果某些事情可能是一个声明,那么它就是一个声明。

编辑:重新解决有关POD和非POD数据的初始化问题,虽然我同意所有已经说过的内容,但我只想指出这些问题仅适用于new'd或以其他方式构造的没有用户定义的构造函数。如果有这样的构造函数,它将被使用。对于99.99%的合理设计类,将会有这样的构造函数,因此可以忽略这些问题。

答案 3 :(得分:16)

通常我们在第一种情况下进行默认初始化,在第二种情况下进行值初始化。

例如: 如果是int(POD类型):

  • int* test = new int - 我们有任何初始化,* test的值可以是任何。

  • int* test = new int() - * test的值为0。

下一个行为取决于您的类型Test。 我们有不同的情况:测试有defult构造函数,Test有生成默认构造函数,Test包含POD成员,非POD成员......

答案 4 :(得分:10)

假设Test是一个具有已定义构造函数的类,则没有区别。后一种形式使得Test的构造函数运行起来更加清晰,但就是这样。

答案 5 :(得分:1)

new的规则类似于使用自动存储持续时间初始化对象时发生的事情(尽管由于令人烦恼的解析,语法可能会略有不同)。

如果我说:

int my_int; // default-initialize → indeterminate (non-class type)

然后my_int具有不确定的值,因为它是非类类型。另外,我可以像这样对my_int进行值初始化(对于非类类型,它为零初始化):

int my_int{}; // value-initialize → zero-initialize (non-class type)

(当然,我不能使用(),因为那将是一个函数声明,但是int()的作用与int{}相同,以构造一个临时项。)

而对于类类型:

Thing my_thing; // default-initialize → default ctor (class type)
Thing my_thing{}; // value-initialize → default-initialize → default ctor (class type)

调用默认构造函数以创建Thing,无一例外。

因此,规则或多或少:

  • 这是类类型吗?
    • :调用默认构造函数,无论它是值初始化(使用{}还是默认初始化(不使用{})。 (通过值初始化还有一些其他的先验归零行为,但默认构造函数始终具有最终决定权。)
    • :是否使用过{}
      • :该对象是值初始化的,对于非类类型,该对象或多或少地只是零初始化。
      • :该对象是默认初始化的,对于非类类型,该对象保留不确定的值(实际上未初始化)。

这些规则精确地转换为new语法,并增加了规则,即()可以代替{},因为new从未被解析为函数声明。所以:

int* my_new_int = new int; // default-initialize → indeterminate (non-class type)
Thing* my_new_thing = new Thing; // default-initialize → default ctor (class type)
int* my_new_zeroed_int = new int(); // value-initialize → zero-initialize (non-class type)
     my_new_zeroed_int = new int{}; // ditto
       my_new_thing = new Thing(); // value-initialize → default-initialize → default ctor (class type)

(此答案包含了C ++ 11中概念上的变化,而当前答案尚不能满足此变化;值得注意的是,现在技术上默认将初始化一个最终值不确定的新标量或POD实例(对于POD类型,从技术上讲是一个琐碎的默认构造函数)。虽然这不会引起行为上的实际改变,但确实可以简化规则。