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

如果'Test'是一个普通类,那么以下两者之间是否有区别:

Test* test = new Test;

而且

Test* test = new Test();
132115 次浏览

不,它们是一样的。但两者之间是有区别的:

Test t;      // create a Test called t

而且

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

这是因为c++(和C)的基本规则:如果某个东西可以是声明,那么它就是声明。

编辑:关于POD和非POD数据的初始化问题,虽然我同意上面所说的一切,但我只想指出,这些问题只适用于新构造或其他构造的东西没有用户定义的构造函数的情况。如果有这样的构造函数,将使用它。99.99%的合理设计的类都有这样的构造函数,因此可以忽略这个问题。

假设Test是一个定义了构造函数的类,没有区别。后一种形式更清楚地表明Test的构造函数正在运行,但仅此而已。

一般来说,第一种情况是默认初始化,第二种情况是值初始化。

例如< p >:如果是int (POD类型):

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

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

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

让我们来研究一下,因为有些差异实际上会影响代码的行为。以下大部分内容都来自于对“新旧事物”的文章的评论。

有时new操作符返回的内存将被初始化,有时则不初始化,这取决于新创建的类型是POD(普通旧数据),还是包含POD成员并使用编译器生成的默认构造函数的类。

  • 在c++ 1998中,有两种初始化类型:0和default
  • 在c++ 2003中增加了第三种初始化类型,值初始化。

假设:

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

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

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

  • 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() - value- initializing B,它对所有字段进行零初始化,因为它的默认ctor是编译器生成的,而不是用户定义的。

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

  • new C() -值初始化C,它调用默认的ctor。

所以在c++的所有版本中,new Anew A()之间是有区别的,因为a是POD。

对于案例new B(),c++ 98和c++ 03之间的行为是不同的。

这是c++的一个让你抓狂的角落。在构造一个对象时,有时你想要/需要paren,有时你绝对不能拥有它们,有时这无关紧要。

new Thing();明确表示您希望调用构造函数,而new Thing;则暗示您不介意不调用构造函数。

如果在带有用户定义构造函数的结构/类上使用,则没有区别。如果在一个平凡的结构体/类上调用(例如struct Thing { int i; };),那么new Thing;就像malloc(sizeof(Thing));,而new Thing();就像calloc(sizeof(Thing));——它得到零初始化。

问题就在这两者之间:

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

在这个例子中,new Thingy;new Thingy();的行为在c++ 98和c++ 2003之间发生了变化。详见第2条。

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{}; // dittomy_new_thing = new Thing(); // value-initialize → default-initialize → default ctor (class type)

(这个答案包含了c++ 11中概念上的变化,上面的答案目前没有;值得注意的是,一个新的标量或POD实例将以一个不确定的值结束,现在技术上是默认初始化的(对于POD类型,技术上调用一个普通的默认构造函数)。虽然这并没有引起行为上的实际变化,但它确实在一定程度上简化了规则。)

我在下面写了一些示例代码,作为Michael Burr回答的补充:

#include <iostream>
struct A1 {int i;int j;};
struct B {int k;B() : k(4) {}B(int k_) : k(k_) {}};
struct A2 {int i;int j;B b;};
struct A3 {int i;int j;B b;A3() : i(1), j(2), b(5) {}A3(int i_, int j_, B b_): i(i_), j(j_), b(b_) {}};
int main() \{\{std::cout << "Case#1: POD without ()\n";A1 a1 = {1, 2};std::cout << a1.i << " " << a1.j << std::endl;A1* a = new (&a1) A1;std::cout << a->i << " " << a->j  << std::endl;}{std::cout << "Case#2: POD with ()\n";A1 a1 = {1, 2};std::cout << a1.i << " " << a1.j << std::endl;A1* a = new (&a1) A1();std::cout << a->i << " " << a->j  << std::endl;}{std::cout << "Case#3: non-POD without ()\n";A2 a1 = {1, 2, {3}};std::cout << a1.i << " " << a1.j << " " << a1.b.k << std::endl;A2* a = new (&a1) A2;std::cout << a->i << " " << a->j << " " << a->b.k << std::endl;}{std::cout << "Case#4: non-POD with ()\n";A2 a1 = {1, 2, {3}};std::cout << a1.i << " " << a1.j << " " << a1.b.k  << std::endl;A2* a = new (&a1) A2();std::cout << a->i << " " << a->j << " " << a1.b.k << std::endl;}{std::cout << "Case#5: user-defined-ctor class without ()\n";A3 a1 = {11, 22, {33}};std::cout << a1.i << " " << a1.j << " " << a1.b.k << std::endl;A3* a = new (&a1) A3;std::cout << a->i << " " << a->j << " " << a->b.k << std::endl;}{std::cout << "Case#6: user-defined-ctor class with ()\n";A3 a1 = {11, 22, {33}};std::cout << a1.i << " " << a1.j << " " << a1.b.k  << std::endl;A3* a = new (&a1) A3();std::cout << a->i << " " << a->j << " " << a1.b.k << std::endl;}return 0;}
/*output with GCC11.1(C++20)Case#1: POD without ()1 21 2Case#2: POD with ()1 20 0Case#3: non-POD without ()1 2 31 2 4Case#4: non-POD with ()1 2 30 0 4Case#5: user-defined-ctor class without ()11 22 331 2 5Case#6: user-defined-ctor class with ()11 22 331 2 5*/