如果'Test'是一个普通的类,有什么区别:
Test* test = new Test;
和
Test* test = new Test();
如果'Test'是一个普通的类,有什么区别:
Test* test = new Test;
和
Test* test = new Test();
让我们变得迂腐,因为存在实际上会影响代码行为的差异。以下大部分内容来自对“旧新事物”文章的评论。
有时 new 运算符返回的内存将被初始化,有时它不会取决于您要更新的类型是POD (plain old data)还是包含 POD 成员的类并且正在使用编译器生成的默认构造函数。
认为:
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()
- value-initializes B,它将所有字段初始化为零,因为它的默认 ctor 是编译器生成的,而不是用户定义的。
new C
- 默认初始化 C,它调用默认 ctor。
new C()
- 值初始化 C,它调用默认的 ctor。因此,在所有版本的 C++ 中,和之间存在差异new A
,new A()
因为 A 是 POD。
对于 case ,C++98 和 C++03 之间的行为存在差异new B()
。
这是 C++ 尘土飞扬的角落之一,会让你发疯。构造对象时,有时您想要/需要括号,有时您绝对不能拥有它们,有时这无关紧要。
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 class
virtual WaxOn();
int i;
};
new Thingy;
在这种情况下, vs的行为new Thingy();
在 C++98 和 C++2003 之间发生了变化。请参阅Michael Burr 的解释,了解如何以及为什么。
不,它们是一样的。但两者之间有区别:
Test t; // create a Test called t
和
Test t(); // declare a function called t which returns a Test
这是因为基本的 C++(和 C)规则:如果某物可能是声明,那么它就是声明。
编辑:关于 POD 和非 POD 数据的初始化问题,虽然我同意所说的一切,但我只想指出,这些问题仅适用于新事物或以其他方式构建的事物没有用户定义的构造函数。如果有这样的构造函数,它将被使用。99.99% 的合理设计的类都会有这样的构造函数,所以这些问题可以忽略。
一般来说,我们在第一种情况下进行默认初始化,在第二种情况下进行值初始化。
例如:如果是 int(POD 类型):
int* test = new int
- 我们有任何初始化,*test 的值可以是任何值。
int* test = new int()
- *test 将有 0 值。
下一个行为取决于您的类型测试。我们有不同的情况:测试有默认构造函数,测试生成默认构造函数,测试包含 POD 成员,非 POD 成员......
假设 Test 是具有定义构造函数的类,则没有区别。后一种形式使 Test 的构造函数正在运行更加清晰,但仅此而已。
的规则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 类型,在技术上调用一个简单的默认构造函数)。虽然这不会导致行为上的太多实际变化,但它确实在一定程度上简化了规则。)
我在下面写了一些示例代码,作为对 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 2
1 2
Case#2: POD with ()
1 2
0 0
Case#3: non-POD without ()
1 2 3
1 2 4
Case#4: non-POD with ()
1 2 3
0 0 4
Case#5: user-defined-ctor class without ()
11 22 33
1 2 5
Case#6: user-defined-ctor class with ()
11 22 33
1 2 5
*/