C++对象初始化(堆栈)

4

今天我看到了一个我不熟悉的C++类初始化。

CPrice price = CPrice();

初始化通常应该像这样:
CPrice price;

我本以为第一个应该会报错之类的。 这里会发生什么?我猜测变量在堆栈上,因为它没有用new初始化。

我使用Visual Studio Express 2012和微软的C++编译器。这可能是微软编译器特有的东西,因此被允许吗?


1
我认为他们正在遵循某种样式指南。如果你在创建变量时使用 =,你就知道它已经初始化了。 - NathanOliver
你为什么认为这会产生错误?CPrice()是一个构造函数,返回一个分配给新声明变量的对象... - 463035818_is_not_a_number
@tobi303 它是通过 = 赋值还是复制("operator=")赋值的? - Bongo
3
这是一个初始化操作,这里没有赋值。 - Alan Stokes
1
@Bongo 不对,实际上在变量声明的上下文中,= 表示复制。根据定义,CPrice price = CPrice();CPrice price(CPrice()); 是相同的。欢迎来到 C++ 语法怪癖的世界。 - luk32
如果您不确定CPrice是否为用户类型(例如,您正在编写模板代码,或者某人已经使用了using CPrice = int),那么第一次初始化更加安全,否则price可能未被初始化。 - Chris Drew
3个回答

8
两种方式都是正确的,从客户端代码的角度来看,它们具有相同的可观察行为:price 是一个类型为 CPrice 的默认构造变量,在堆栈上分配。
如果您想深入技术细节,它们并不完全相同:
CPrice price; 是对类型为 CPrice 的变量 price 进行默认初始化。这是一个用户类型(即类),所以它总是意味着调用默认构造函数。
CPrice price = CPrice(); 是一个复合表达式,它执行两个操作:
1. CPrice():通过直接初始化(调用带 () 的构造函数)初始化一个匿名的 CPrice 对象(在堆栈上)。由于括号为空,这将调用默认构造函数。 2. 然后,初始化一个类型为 CPrice 的变量 price,使用复制初始化(在 C++11 之前)/移动初始化(从 C++11 开始提供),复制/移动来源对象是匿名的 CPrice 实例。
最长的分配强制要求必须存在 CPrice 的复制构造函数,否则代码将出错。但是编译器可以跳过复制构造并将其优化掉,通过发出与最短形式相同的代码。
此外,在 C++11 中,如果 CPrice 存在移动构造函数,则将在此情况下使用它代替复制构造函数(也就是说,如果此操作完全被删除,则不会使用它)。
因此,唯一可感知的区别是最短形式即使 CPrice 不可复制构造也会编译。两种形式都需要 CPrice 可以默认构造。
还有一个或多个相关的精度来自其他答案。您可以认为这样一个“假设”的中间地带声明是相同的:
CPrice price();

然而,实际上完全不同:这个声明将 price 定义为一个不带参数的函数(空括号),返回值为 CPrice。俗称为“最棘手的解析”。

2
很有可能复制将被省略,与第二个示例相同。 - NathanOliver
1
即使复制被省略了(我同意它几乎肯定会被省略),相关的复制构造函数必须存在并且可调用,所以这并不完全相同。 - Alan Stokes
谢谢您的评论!@NathanOliver:我想我们是同时在写它;) - Ad N
1
在C++11中,CPrice price = CPrice(); price可能会被移动构造,在此处查看默认生成的移动构造函数 这里 - Allanqunzi
1
这方面有一篇很棒的Herb Sutter GOTW文章。虽然你已经涵盖了他在那里提到的几乎所有内容,但对于任何对此主题感到困惑的人来说,阅读一下仍然是值得的:http://herbsutter.com/2013/05/09/gotw-1-solution/ - schanq
显示剩余2条评论

-1

赋值操作会在实例已经被声明时调用赋值运算符(如果未被删除)。第一行声明并显式调用构造函数,在堆上使用多态性更有帮助,但在栈上不是很有用。第二行直接调用其默认构造函数(如果未被删除且未实现operator())。

如果您仍然想知道operator=,则在类声明中显式声明
CPrice & operator = (const CPrice &) = delete;


"具有多态性的堆" 实例的存储与多态性有什么关系? - πάντα ῥεῖ

-2
这两行代码实现了相同的功能。它们都调用默认构造函数(不带参数的构造函数)。另外,也可以使用CPrice price();,因为这个和你第一个例子一样,可以在有参数的情况下传递参数,而第二个则不能。此外,在像这样的简单情况下,如果没有使用new关键字,则分配将在堆栈上进行。

1
你所说的“simple things like this”是什么意思? - 463035818_is_not_a_number
只是简单的对象创建。在C++中,似乎有三种做事情的方式,所以我主要是为了防止有人告诉我new不是在堆上创建新对象的唯一方法。 - Benjamin James Drury
2
CPrice price(); 并不是你想象中的那样:它是一个声明了一个名为 price 的函数,该函数不带参数并返回一个 CPrice 对象。请参阅 most vexing parse 文章。 - Nik Bougalis
1
“这两行代码执行的功能完全相同。”并不是这样。它们有不同的含义,尽管编译器允许优化第二个代码,使其在实际上与第一个代码相同。CPrice price = CPrice();CPrice price(CPrice());在定义上是相同的,而CPrice price;则在原则上有所不同。 - luk32
1
那样的话,今天我学到了新的东西。我想,最终的结果是完全相同的,对吗? - Benjamin James Drury
显示剩余2条评论

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