是矩形 a = 矩形(3, 4);等价于矩形 A(3,4);?

Is Rectangle A = Rectangle(3, 4); equivalent to Rectangle A(3,4);?

提问人:Hanfei Sun 提问时间:7/4/2017 最后编辑:Baum mit AugenHanfei Sun 更新时间:7/4/2017 访问量:370

问:

以下是我的代码:

#include <iostream>
using namespace std;

class Rectangle {
  int width, height;
 public:
  Rectangle(int, int);
  int area() { return (width * height); }
};

Rectangle::Rectangle(int a, int b) {

  width = a;
  height = b;
}

int main() {
  Rectangle A(3, 4);
  Rectangle B = Rectange(3,4);
  return 0;
}

我没有为类定义任何复制构造函数或赋值运算符。Rectangle

真的在连续剧中做了三件事吗?Rectangle B = Rectangle(3, 4);

  1. 为 Rectangle 的临时变量(让我们用它来表示它)分配内存空间,调用以初始化它。tmpRectangle::Rectangle(3, 4)

  2. 为变量分配内存空间,使用默认构造函数对其进行初始化B

  3. (会员)使用赋值运算符复制到tmpBRectangle& operator = (const Rectangle &)

这个解释有意义吗?我想我可能理解错了,因为与 .Rectangle A(3, 4);

有人对此有想法吗?真的等同于吗?谢谢!Rectangle A(3,4)Rectangle A = Rectangle(3, 4);

C++ 初始化 赋值运算符

评论

0赞 Hanfei Sun 7/4/2017
@JerryCoffin 感谢您的更正!我混淆了 Java 语法......

答:

-3赞 Catau 7/4/2017 #1
  1. Rectangle* C = new Rectangle(3,4);,则代码中存在语法错误。
  2. operator =(Rectangle& rho)是默认定义的返回引用。因此,不会如上所述创建对象。Rectangle B = Rectangle(3, 4);tmp

评论

0赞 Lightness Races in Orbit 7/4/2017
1. 这不是语法错误。2.与它无关operator=
0赞 Catau 7/4/2017
帖子说我知道这是错误的。Rectangle C = new Rectangle(3,4);
1赞 Catau 7/4/2017
我写了一个带有打印消息的类,结果显示您的评论是正确的。多谢。operator=
3赞 Lightness Races in Orbit 7/4/2017 #2

真的在连续剧中做了三件事吗?Rectangle B = Rectangle(3, 4);

不,这不是真的,但这并不是你的错:这令人困惑。

T obj2 = obj1不是赋值,而是初始化

所以你是对的,它将首先创建(在你的情况下,是临时的),但将使用复制构造函数构造,而不是默认构造然后分配给。obj1obj2


对于 ,唯一的区别是临时变量的内存空间分配给堆而不是堆栈。Rectangle C = new Rectangle(3, 4);

不,它不会编译,但会编译。这里已经有很多关于动态分配的解释。Rectangle* C

评论

0赞 Hanfei Sun 7/4/2017
谢谢,那么相当于?如果不是,首选哪一个?似乎更常用,对吧?Rectangle A(3,4)Rectangle A = Rectangle(3, 4);Rectangle A(3,4)
0赞 Lightness Races in Orbit 7/4/2017
@HanfeiSun:不完全等同(直到 C++17),但在这种情况下,功能相同是。是的。
3赞 Jerry Coffin 7/4/2017 #3

在这种情况下,实际发生的事情和理论上发生的事情之间存在显着差异。

第一种很简单:只需构造一个 Rectangle 并初始化为 和 。这一切都是使用您定义的构造函数在“一步”中完成的。简单明了 - 所以在可能的情况下,它通常是首选和推荐的。Rectangle A(3, 4);widthheight34Rectangle(int, int);

然后让我们考虑一下: .从理论上讲,这会构造一个临时对象,然后从该临时对象复制构造。Rectangle B = Rectangle(3,4);B

在实践中,编译器会检查它是否能够创建临时对象,以及它是否能够使用复制构造函数从该临时对象进行初始化。在检查了这是否可行之后,几乎任何称职的编译器(至少在启用优化时,通常甚至在未启用优化时)都会生成与创建代码基本相同的代码。BA

但是,如果删除复制构造函数,则通过添加:

Rectangle(Rectangle const &) = delete;

...然后编译器会发现它无法从临时复制构造,并且会拒绝编译代码。即使最终生成的代码从未实际使用过复制构造函数,它也必须可用才能正常工作。B

最后,让我们看一下第三个示例(语法已更正):

Rectangle *C = new Rectangle(3, 4);

尽管看起来有点像上面创建的线,但所涉及的结构实际上更像您用来创建的前一个。只创建一个对象(即使在理论上也是如此)。它是从免费存储区分配的,并使用构造函数直接初始化。BARectangle(int, int);

然后,该对象的地址用于初始化(这只是一个指针)。就像 的初始化一样,即使删除了 的复制构造函数,这也将起作用,因为即使在理论上也没有涉及的复制。CARectangleRectangle

这些都不涉及赋值运算符。如果要删除赋值运算符:

Rectangle &operator=(Rectangle const &) = delete;

...所有这些代码仍然可以,因为(尽管使用了 )代码中的任何位置都没有赋值。=

要使用作业(如果你真的坚持这样做),你可以(例如)做这样的事情:

Rectangle A(3, 4);
Rectangle B = Rectangle(5, 6);
B = A;

这仍然只使用构造函数来创建和初始化 和 ,但随后使用赋值运算符将 的值赋值给 。在这种情况下,如果如上所示删除赋值运算符,则代码将失败(不会编译)。ABAB

我们的一些误解似乎源于这样一个事实,即如果您不采取任何措施来阻止编译器这样做,它会自动为您创建“特殊成员函数”。阻止它这样做的方法之一是上面显示的语法,但这不是唯一的语法。例如,如果类包含引用类型的成员变量,则编译器不会为您创建赋值运算符。如果你从这样简单的事情开始:= delete;

struct Rectangle { 
    int width, height;
};

...编译器将完全自动生成默认构造函数、复制构造函数、移动构造函数、复制赋值和移动赋值运算符。

2赞 Barry 7/4/2017 #4

Rectangle A(3, 4);始终简单地调用构造函数,贯穿 C++ 具有构造函数的所有历史。简单。无聊的。Rectangle(int, int)

现在是有趣的部分。

C++17

在最新版(在撰写本文时)标准中,无论 的移动或复制构造函数的性质是什么,都会立即折叠成 This happens 。此功能通常称为保证复制省略,但需要强调的是,这里没有复制,也没有移动。发生的情况是从 直接初始化的。Rectangle B = Rectangle(3,4);Rectangle B(3,4);RectangleB(3,4)

C++之前的17

在 C++17 之前,有一个临时构造,编译器可以对其进行优化(我的意思是它肯定会,除非你告诉它不要这样做)。但是您的事件顺序不正确。需要注意的是,这里不会发生任何分配。我们没有分配给 .我们正在建设.表格代码:RectangleBB

T var = expr;

是复制初始化,它不是复制分配。因此,我们做两件事:

  1. 我们使用构造函数构造一个临时的RectangleRectangle(int, int)
  2. 该临时绑定直接绑定到隐式生成的移动(或复制,C++ 11 之前)构造函数中的引用,然后调用该构造函数 - 从临时构造函数执行成员级移动(或复制)。(或者,更准确地说,重载解析会选择给定 prvalue 的最佳构造函数RectangleRectangle)
  3. 临时在语句处被销毁。

如果删除了移动构造函数(或者,在 C++ 11 之前,复制构造函数被标记),则尝试以这种方式构造的格式不正确。如果不理会特殊成员函数(如本例中所示),则 of 的两个声明肯定会编译为相同的代码。privateBAB


如果实际删除类型,初始化的形式可能看起来更熟悉:B

auto B = Rectangle(3,4);

这就是 Herb Sutter 喜欢的所谓 AAA(几乎总是自动)风格的声明。这与 完全相同,只是首先推导 as 的类型。Rectangle B = Rectangle(3, 4)BRectangle

评论

0赞 Lightness Races in Orbit 7/4/2017
“矩形A(3,4);总是简单地调用 Rectangle(int, int) 构造函数,贯穿 C++ 具有构造函数的所有历史。简单。无聊。嗯,不,它的作用远不止于此。
0赞 Lightness Races in Orbit 7/4/2017
“在最新版(截至撰写本文时)标准”C++17 尚不存在。C++14 是该标准的最新版本(截至撰写本文时)。
0赞 Lightness Races in Orbit 7/4/2017
“编译器可以优化(我的意思是,它肯定会,除非你告诉它不要这样做)”是的,但仍然存在“可见效果”,例如要求可访问复制/移动 CTOR。
0赞 Lightness Races in Orbit 7/4/2017
“我们使用 Rectangle(int, int) 构造函数构造一个临时矩形”技术上是准确的(因为该 ctor 将被调用),但有点误导:我们从不直接调用构造函数,但这种措辞听起来像我们这样做
0赞 Lightness Races in Orbit 7/4/2017
“临时在声明中被摧毁。”在末尾*