简体   繁体   English

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

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

Below is my code: 下面是我的代码:

#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;
}

I didn't define any copy constructors or assignment operator for Rectangle class. 我没有为Rectangle类定义任何复制构造函数或赋值运算符。

Is it true that Rectangle B = Rectangle(3, 4); Rectangle B = Rectangle(3, 4); actually does three things in serial? 其实是连续做三件事吗?

  1. Allocate memory space for a temporary variable (let's use tmp to denote it) of Rectangle, call Rectangle::Rectangle(3, 4) to intialize it. 为Rectangle的临时变量(让我们用tmp表示)分配内存空间,调用Rectangle::Rectangle(3, 4)初始化它。

  2. Allocate memory space for variable B , initialize it with the default constructor 为变量B分配内存空间,并使用默认构造函数对其进行初始化

  3. (memberwise) Copy tmp to B with the assignment operator Rectangle& operator = (const Rectangle &) (以成员身份)使用赋值运算符Rectangle& operator = (const Rectangle &)tmp复制到B

Does that explanation make sense? 这种解释有意义吗? I think I might understand wrongly because this process looks very clumsy and inefficient compared with Rectangle A(3, 4); 我认为我可能会理解错误,因为与Rectangle A(3, 4);相比,此过程看起来非常笨拙且效率低下Rectangle A(3, 4); .

Does anyone have ideas about this? 有人对此有想法吗? Is it true that Rectangle A(3,4) is equivalent to Rectangle A = Rectangle(3, 4); Rectangle A(3,4)等于Rectangle A = Rectangle(3, 4); Rectangle A(3,4)是真的吗Rectangle A = Rectangle(3, 4); ? Thanks! 谢谢!

Is it true that Rectangle B = Rectangle(3, 4); Rectangle B = Rectangle(3, 4); actually does three things in serial? 其实是连续做三件事吗?

No, that isn't true, but it's not really your fault: this is confusing. 不,那不是真的,但这并不是你的错:这很令人困惑。

T obj2 = obj1 is not an assignment, but an initialisation . T obj2 = obj1不是赋值,而是初始化

So you're right that obj1 will be created first (in your case, a temporary), but obj2 will be constructed using the copy constructor , not default constructed then assigned-to. 因此,您将首先创建obj1是正确的(在您的情况下是临时的),但是obj2将使用copy构造函数构造 ,而不是默认构造然后分配给。


For Rectangle C = new Rectangle(3, 4); 对于Rectangle C = new Rectangle(3, 4); , the only difference is that the memory space of the temporary variable is allocated to the heap instead of stack. ,唯一的区别是临时变量的内存空间分配给了堆而不是堆栈。

No, it won't compile, but Rectangle* C would. 不,它不会编译,但是Rectangle* C可以。 There are plenty of explanations of dynamic allocation here already. 这里已经有很多关于动态分配的解释。

In this case, there's a significant difference between what actually happens, and what theoretically sort of happens. 在这种情况下, 实际发生的情况与理论上发生的情况之间存在显着差异。

The first is simple: Rectangle A(3, 4); 第一个很简单: Rectangle A(3, 4); just constructs a Rectangle with width and height initialized to 3 and 4 . 只是构造一个Rectangle,其widthheight初始化为34 It's all done in "one step" using the Rectangle(int, int); 所有操作都使用Rectangle(int, int);在“一步”中完成Rectangle(int, int); constructor you defined. 您定义的构造函数。 Simple and straightforward--so it's what's generally preferred and recommended when it's possible. 简单明了-因此尽可能地推荐并推荐它。

Then let's consider: Rectangle B = Rectangle(3,4); 然后让我们考虑: Rectangle B = Rectangle(3,4); . In theory, this constructs a temporary object, then copy-constructs B from that temporary. 从理论上讲,这将构造一个临时对象,然后从该临时对象复制构造B

In practice, what happens is that the compiler checks that it would be able to create the temporary object, and that it would be able to use the copy constructor to initialize B from that temporary. 在实践中,会发生什么情况是,编译器检查,这能够创建临时对象,而且它是能够使用拷贝构造函数初始化B从暂时的。 Having checked that this would be possible, nearly any competent compiler (at least when optimization is enabled, and often even when it's not) will generate code essentially identical to what it did for creating A . 在检查了这是可能的之后,几乎所有有能力的编译器(至少在启用优化时,甚至在没有启用优化时)都将生成与创建A基本上相同的代码。

If, however, you delete the copy constructor, by adding: 但是,如果通过添加以下内容来删除复制构造函数:

Rectangle(Rectangle const &) = delete;

...then the compiler will find that it can't copy construct B from the temporary, and will refuse to compile the code. ...然后,编译器会发现它无法从临时文件中复制构造B ,并拒绝编译代码。 Even if the final, generated code never actually uses the copy constructor, it has to be available for this to work. 即使最终生成的代码从未真正使用过副本构造函数,它也必须可用才能起作用。

Finally, let's look at your third example (with the syntax corrected): 最后,让我们看看您的第三个示例(语法已更正):

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

Despite looking somewhat like the line above that created B , the construction involved is really much more like the previous one that you used to create A . 尽管看上去有点像创建B那条线,但是涉及的构造实际上更像您用来创建A Only one object is (even theoretically) created. (即使在理论上)仅创建一个对象。 It's allocated from the free store and directly initialized using your Rectangle(int, int); 它是从免费商店中分配的,并使用您的Rectangle(int, int);直接初始化Rectangle(int, int); constructor. 构造函数。

Then the address of that object is used to initialize C (which is only a pointer). 然后,该对象的地址用于初始化C (仅是一个指针)。 Like the initialization of A , this will work even if Rectangle 's copy constructor is deleted, because even in theory there's no copying of a Rectangle involved. A的初始化一样,即使删除Rectangle的copy构造函数,它也将起作用,因为即使从理论上讲,也不会涉及Rectangle的复制。

None of these involves an assignment operator. 这些都不涉及赋值运算符。 If you were to delete the assignment operator: 如果要删除分配运算符:

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

...all this code would still be fine, because (despite the use of the = ) there's no assignment anywhere in the code. ...所有这些代码仍然可以,因为(尽管使用了= )在代码的任何地方都没有赋值。

To use an assignment (if you really insisted on doing so) you could (for example) do something like: 要使用分配(如果您确实坚持这样做),可以(例如)执行以下操作:

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

This still uses only constructors to create and initialize A and B , but then uses the assignment operator to assign the value of A to B . 这仍然仅使用构造函数来创建和初始化AB ,但是随后使用赋值运算符将A的值赋给B In this case, if you deleted the assignment operator as shown above, the code would fail (would not compile). 在这种情况下,如果您如上所示删除了赋值运算符,则代码将失败(将无法编译)。

Some of our misunderstanding seems to stem from the fact that the compiler with automatically create "special member functions" for you if you don't do something to prevent it from doing so. 我们的一些误解似乎源于以下事实:如果您不采取任何措施阻止编译器自动创建“特殊成员函数”,则会自动为您创建“特殊成员函数”。 One of the ways you can prevent it from doing so is the = delete; 防止它发生的一种方法是= delete; syntax shown above, but that's not the only one. 上面显示的语法,但这不是唯一的一种。 For example, if your class includes a member variable of reference type, the compiler won't create an assignment operator for you. 例如,如果您的类包含引用类型的成员变量,则编译器不会为您创建赋值运算符。 If you start with something simple like this: 如果从这样简单的事情开始:

struct Rectangle { 
    int width, height;
};

...the compiler will generate a default constructor, copy constructor, move constructor, copy assignment and move assignment operators, all entirely automatically. ...编译器将完全自动生成默认构造函数,复制构造函数,移动构造函数,复制赋值和移动赋值运算符。

Rectangle A(3, 4); always simply invokes the Rectangle(int, int) constructor, throughout all of the history of C++ having constructors. 在具有构造函数的C ++的所有历史中,总是总是简单地调用Rectangle(int, int)构造函数。 Simple. 简单。 Boring. 无聊

Now for the fun part. 现在是有趣的部分。

C++17 C ++ 17

In the newest (as of this writing) edition of the standard, Rectangle B = Rectangle(3,4); 在该标准的最新版本(截至撰写本文时)中, Rectangle B = Rectangle(3,4); immediately collapses down into Rectangle B(3,4); 立即崩溃成Rectangle B(3,4); This happens regardless of what the nature of Rectangle 's move or copy constructors is. 无论Rectangle的move或copy构造函数的性质如何,都会发生这种情况。 This feature is commonly referred to as guaranteed copy elision, although it's important to stress that there is no copy and no move here. 此功能通常被称为保证复制省略,尽管需要强调的是此处没有复制,也没有移动。 What happens is B is direct-initialized from (3,4) . B是从(3,4)直接初始化的。

pre-C++17 C ++ 17之前的版本

Before C++17, there is a temporary Rectangle constructed that a compiler may optimize away (and by may, I mean it will certainly, unless you tell it not to). 在C ++ 17之前,有一个临时的Rectangle构造,编译器可以优化它(可能,我的意思是肯定会的,除非您告诉它不要这样做)。 But your ordering of events is not correct. 但是您对事件的排序不正确。 It's important to note that no assignment happens here . 重要的是要注意, 这里没有作业发生 We are not assigning to B . 我们没有分配给B We are constructing B . 我们正在构造B Code of the form: 形式的代码:

T var = expr;

is copy- initialization , it is not copy-assignment. 是复制初始化 ,不是复制分配。 Hence, we do two things: 因此,我们做两件事:

  1. We construct a temporary Rectangle using the Rectangle(int, int) constructor 我们使用Rectangle(int, int)构造函数构造一个临时Rectangle
  2. That temporary binds directly to the reference in the implicitly generated move (or copy, pre-C++11) constructor, which is then invoked - doing a member-wise move (or copy) from the temporary. 该临时项直接绑定到隐式生成的move(或副本,C ++ 11之前的版本)构造函数中的引用,然后调用该构造函数-从临时项进行成员级移动(或副本)。 (Or, more precisely, overload resolution selects the best Rectangle constructor given a prvalue of type Rectangle ) (或者,更准确地说,在给定Rectangle类型的prvalue的情况下,重载分辨率选择最佳的Rectangle构造函数)
  3. The temporary is destroyed at the statement. 该语句将临时文件销毁。

If the move constructor is deleted (or, pre-C++11, the copy constructor is marked private ), then trying to construct B in this way is ill-formed. 如果删除了移动构造函数(或在C ++ 11之前,将复制构造函数标记为private ),则尝试以这种方式构造B是错误的形式。 If the special member functions are left alone (as they are in this example), the two declarations of A of B will certainly compile to the same code. 如果保留特殊成员函数(如本例所示),则A的两个声明B肯定会编译为相同的代码。


The form of initialization in B may look more familiar if you actually drop the type: 如果您实际删除类型,则B的初始化形式可能看起来更熟悉:

auto B = Rectangle(3,4);

This is the so-called AAA (Almost Always Auto) style of declarations that Herb Sutter is a fan of. 这是Herb Sutter拥护的所谓AAA(几乎始终为自动)样式。 This does exactly the same thing as Rectangle B = Rectangle(3, 4) , just with the leading step of first deducing the type of B as Rectangle . 这与Rectangle B = Rectangle(3, 4)功能完全相同,只是第一步是首先将B的类型推导为Rectangle

  1. Rectangle* C = new Rectangle(3,4); , there's grammar error in your code. ,您的代码中有语法错误。
  2. operator =(Rectangle& rho) is by-default defined returning reference. operator =(Rectangle& rho)是默认定义的返回参考。 Thus, Rectangle B = Rectangle(3, 4); 因此, Rectangle B = Rectangle(3, 4); won't create tmp object as you described above. 不会如上所述创建tmp对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM