[英]Is Rectangle A = Rectangle(3, 4); equivalent to Rectangle A(3,4);?
下面是我的代码:
#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);
其实是连续做三件事吗?
为Rectangle的临时变量(让我们用tmp
表示)分配内存空间,调用Rectangle::Rectangle(3, 4)
初始化它。
为变量B
分配内存空间,并使用默认构造函数对其进行初始化
(以成员身份)使用赋值运算符Rectangle& operator = (const Rectangle &)
将tmp
复制到B
这种解释有意义吗? 我认为我可能会理解错误,因为与Rectangle A(3, 4);
相比,此过程看起来非常笨拙且效率低下Rectangle A(3, 4);
。
有人对此有想法吗? Rectangle A(3,4)
等于Rectangle A = Rectangle(3, 4);
Rectangle A(3,4)
是真的吗Rectangle A = Rectangle(3, 4);
? 谢谢!
Rectangle B = Rectangle(3, 4);
其实是连续做三件事吗?
不,那不是真的,但这并不是你的错:这很令人困惑。
T obj2 = obj1
不是赋值,而是初始化 。
因此,您将首先创建obj1
是正确的(在您的情况下是临时的),但是obj2
将使用copy构造函数构造 ,而不是默认构造然后分配给。
对于
Rectangle C = new Rectangle(3, 4);
,唯一的区别是临时变量的内存空间分配给了堆而不是堆栈。
不,它不会编译,但是Rectangle* C
可以。 这里已经有很多关于动态分配的解释。
在这种情况下, 实际发生的情况与理论上发生的情况之间存在显着差异。
第一个很简单: Rectangle A(3, 4);
只是构造一个Rectangle,其width
和height
初始化为3
和4
。 所有操作都使用Rectangle(int, int);
在“一步”中完成Rectangle(int, int);
您定义的构造函数。 简单明了-因此尽可能地推荐并推荐它。
然后让我们考虑: Rectangle B = Rectangle(3,4);
。 从理论上讲,这将构造一个临时对象,然后从该临时对象复制构造B
在实践中,会发生什么情况是,编译器检查,这将能够创建临时对象,而且它是能够使用拷贝构造函数初始化B
从暂时的。 在检查了这是可能的之后,几乎所有有能力的编译器(至少在启用优化时,甚至在没有启用优化时)都将生成与创建A
基本上相同的代码。
但是,如果通过添加以下内容来删除复制构造函数:
Rectangle(Rectangle const &) = delete;
...然后,编译器会发现它无法从临时文件中复制构造B
,并拒绝编译代码。 即使最终生成的代码从未真正使用过副本构造函数,它也必须可用才能起作用。
最后,让我们看看您的第三个示例(语法已更正):
Rectangle *C = new Rectangle(3, 4);
尽管看上去有点像创建B
那条线,但是涉及的构造实际上更像您用来创建A
。 (即使在理论上)仅创建一个对象。 它是从免费商店中分配的,并使用您的Rectangle(int, int);
直接初始化Rectangle(int, int);
构造函数。
然后,该对象的地址用于初始化C
(仅是一个指针)。 像A
的初始化一样,即使删除Rectangle
的copy构造函数,它也将起作用,因为即使从理论上讲,也不会涉及Rectangle
的复制。
这些都不涉及赋值运算符。 如果要删除分配运算符:
Rectangle &operator=(Rectangle const &) = delete;
...所有这些代码仍然可以,因为(尽管使用了=
)在代码的任何地方都没有赋值。
要使用分配(如果您确实坚持这样做),可以(例如)执行以下操作:
Rectangle A(3, 4);
Rectangle B = Rectangle(5, 6);
B = A;
这仍然仅使用构造函数来创建和初始化A
和B
,但是随后使用赋值运算符将A
的值赋给B
在这种情况下,如果您如上所示删除了赋值运算符,则代码将失败(将无法编译)。
我们的一些误解似乎源于以下事实:如果您不采取任何措施阻止编译器自动创建“特殊成员函数”,则会自动为您创建“特殊成员函数”。 防止它发生的一种方法是= delete;
上面显示的语法,但这不是唯一的一种。 例如,如果您的类包含引用类型的成员变量,则编译器不会为您创建赋值运算符。 如果从这样简单的事情开始:
struct Rectangle {
int width, height;
};
...编译器将完全自动生成默认构造函数,复制构造函数,移动构造函数,复制赋值和移动赋值运算符。
Rectangle A(3, 4);
在具有构造函数的C ++的所有历史中,总是总是简单地调用Rectangle(int, int)
构造函数。 简单。 无聊
现在是有趣的部分。
在该标准的最新版本(截至撰写本文时)中, Rectangle B = Rectangle(3,4);
立即崩溃成Rectangle B(3,4);
无论Rectangle
的move或copy构造函数的性质如何,都会发生这种情况。 此功能通常被称为保证复制省略,尽管需要强调的是此处没有复制,也没有移动。 B
是从(3,4)
直接初始化的。
在C ++ 17之前,有一个临时的Rectangle
构造,编译器可以优化它(可能,我的意思是肯定会的,除非您告诉它不要这样做)。 但是您对事件的排序不正确。 重要的是要注意, 这里没有作业发生 。 我们没有分配给B
我们正在构造B
形式的代码:
T var = expr;
是复制初始化 ,不是复制分配。 因此,我们做两件事:
Rectangle(int, int)
构造函数构造一个临时Rectangle
Rectangle
类型的prvalue的情况下,重载分辨率选择最佳的Rectangle
构造函数) 如果删除了移动构造函数(或在C ++ 11之前,将复制构造函数标记为private
),则尝试以这种方式构造B
是错误的形式。 如果保留特殊成员函数(如本例所示),则A
的两个声明B
肯定会编译为相同的代码。
如果您实际删除类型,则B
的初始化形式可能看起来更熟悉:
auto B = Rectangle(3,4);
这是Herb Sutter拥护的所谓AAA(几乎始终为自动)样式。 这与Rectangle B = Rectangle(3, 4)
功能完全相同,只是第一步是首先将B
的类型推导为Rectangle
。
Rectangle* C = new Rectangle(3,4);
,您的代码中有语法错误。 operator =(Rectangle& rho)
是默认定义的返回参考。 因此, Rectangle B = Rectangle(3, 4);
不会如上所述创建tmp
对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.