为什么你不需要使用"new"关键字来创建对象?

96
这是一个非常简单的问题,但是我已经好几年没有好好学习C++了,所以对此有些困惑。而且,这也不是最容易在互联网上查找到的东西(至少对我来说不是)。
为什么这里没有使用"new"关键字,它是如何工作的呢?
基本上,这里发生了什么?
CPlayer newPlayer = CPlayer(position, attacker);
4个回答

81

这个表达式:

CPlayer(position, attacker)

使用上述构造函数创建类型为CPlayer的临时对象,然后:

CPlayer newPlayer =...;

提到的临时对象使用拷贝构造函数复制到 newPlayer。更好的方法是编写以下代码以避免使用临时对象:

CPlayer newPlayer(position, attacker);

7
实际上,编译器可能会对其进行优化。在这种情况下,复制构造函数不会被调用。 - BostonLogan
7
在声明中使用赋值语句并不比使用构造函数语法低效。如果它们是分开的语句,那么关于临时对象的评论就是正确的。本质上,这声明了一个CPlayer(通常在堆栈上),而不是从自由存储区(堆)分配空间。 - Adrian McCarthy
2
我认为这个答案并不完全正确。虽然OP的代码看起来像是创建了一个临时对象并将其复制,但标准12.1.11表明情况并非如此。这只是一个普通的构造函数调用,正如jleedev所发现的那样。 - Michael Kristofik
10
编译器可以决定是否省略复制(在允许的情况下)。12.8/15。 - Steve Jessop
1
我喜欢 CPlayer newPlayer(position, attacker); 这种方式,因为这种风格在创建堆栈变量时保持了 C++ 代码风格与 C 相同。使用 CPlayer newPlayer = CPlayer(position, attacker); 是不必要的,可能会令人困惑。 - tonga
显示剩余4条评论

71

上述代码在堆栈上构建了一个CPlayer对象,因此不需要使用new。只有在尝试在堆上分配CPlayer对象时才需要使用new。如果您使用堆分配,则代码如下:

CPlayer *newPlayer = new CPlayer(position, attacker);

请注意,在这种情况下,我们使用了指向 CPlayer 对象的指针,需要通过匹配调用 delete 来清理该对象。在堆栈上分配的对象将在其作用域结束时自动销毁。

实际上,更容易且更明显的是,写成:

CPlayer newPlayer(position, attacker);
很多编译器都会对你发布的版本进行优化,而且这种写法更易于阅读。

2
我认为这不正确:“在堆上分配的对象将在超出其作用域时自动销毁。” - Valentin
2
你说得对,我本来想写“堆栈”,而不是“堆”。谢谢你指出来。 - Timo Geusch
同意这个观点。我们应该保持C++代码与C代码相同的风格。因此,如果您想创建堆栈变量,CPlayer newPlayer(position, attacker);CPlayer newPlayer = CPlayer(position, attacker);更好。 - tonga

12
CPlayer newPlayer = CPlayer(position, attacker);

这行代码创建了一个类型为CPlayer的新本地对象。尽管其看起来像函数,但实际上只是调用了CPlayer的构造函数。没有临时对象或复制涉及其中。名为newPlayer的对象存在于其所在的作用域内。你不需要使用new关键字,因为C++不是Java。

CPlayer* newPlayer = new CPlayer(position, attacker);

这行代码在堆上构造了一个名为CPlayer的对象,并定义了一个名为newPlayer的指针来指向它。该对象将一直存在,直到有人使用delete删除它。


4
“没有涉及临时变量或复制”--这并不完全正确。这并没有得到保证。但是,每个体面的编译器都应该省略复制。 - sellibitze
重新学习C++,已经有十多年.NET开发经验了。大约20年前在大学里学过C++,但自那以后就没有再碰过它。堆分配引用的原因是什么?比如说一个类级别的变量,你可能会在各种函数或其他类中访问它?相对于只在单个函数中使用的栈分配变量,有必要将其分配到堆上吗? - clamum
1
@clamum 这是一个单独的问题,已经有很多答案在SO上了。这里有一个好的答案:https://dev59.com/uGw15IYBdhLWcg3wntAQ#6500497 - Michael Kristofik

7

newPlayer不是动态分配的变量,而是自动、堆栈分配的变量:

CPlayer* newPlayer = new CPlayer(pos, attacker);

与...不同

CPlayer newPlayer = CPlayer(pos, attacker);

通过普通的CPlayer(position, attacker)构造函数调用,newPlayer在堆栈上分配,虽然比通常的方法略显冗长。

CPlayer newPlayer(pos, attacker);

这基本上就是说:

int i = int(3);

2
小心这里。这是“复制初始化”。它不是赋值。 - sellibitze
我改口了,这不是一个赋值;甚至复制构造函数也没有涉及。请相应地编辑答案。 - digitalarbeiter
2
当然,复制构造函数(至少在逻辑上)是涉及其中的。如果您将复制构造函数设置为私有,那么您会注意到这个复制初始化将不再起作用。即使编译器能够优化掉复制操作,C++标准仍要求提供可访问的复制构造函数。 - sellibitze
不,new 在堆上分配内存,你的第二个玩家在栈上。 - user90843

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