我写了这段代码
#include<iostream>
using namespace std;
class circle
{ public:
int radius;
public:
circle()
{ cout<<"object address-"<<this<<endl;
}
circle(int r)
{ radius=r;
cout<<"object address-"<<this<<" and radius is "<<this->radius<<endl;
}
circle operator = (circle c)
{ cout<<"my object is "<<this<<endl;
cout<<"my arg is "<<&c<<endl;
radius=c.radius;
return circle(radius);
}
};
int main()
{ cout<<"creating circle c1 with radius 10"<<endl;
circle c1(10);
cout<<"creating empty circle c2"<<endl;
circle c2;
cout<<"creating empty circle c3"<<endl;
circle c3;
cout<<"c3=c2=c1"<<endl;
c3=c2=c1;
cout<<"Final values"<<endl<<"c1 radius-"<<c1.radius<<endl<<"c2 radius-"<<c2.radius<<endl<<"c3 radius-"<<c3.radius<<endl;
return 0;
}输出是
creating circle c1 with radius 10
object address-0xffffcbfc and radius is 10
creating empty circle c2
object address-0xffffcbf8
creating empty circle c3
object address-0xffffcbf4
c3=c2=c1
my object is 0xffffcbf8
my arg is 0xffffcbd8
object address-0xffffcbbc and radius is 10
my object is 0xffffcbf4
my arg is 0xffffcbd8
object address-0xffffcbbc and radius is 10
Final values
c1 radius-10
c2 radius-10
c3 radius-10看来它起作用了。
问题1)为了避免创建variables.Can的副本,我更倾向于使用引用,我说我的程序很好,但效率不高?
问题2)请指出我理解中的缺陷。
我想接下来会发生
0xffffcbd 8是0xffffcbfc (圆形c1)的副本。
首先发生c2.operator=(复制c1),然后将c2的半径设置为c1的半径,并返回新的对象0 0xffffcbbc。
我希望现在在-c3.operator=(某个圆圈)
一些圆圈应该是0xffffcbbc的副本。
但是,圆c1的原版似乎是“某个圆”,这使得我认为c3.operator=(复制c1)正在发生。是这样吗?
赋值运算符的返回值似乎不在任何地方使用。那是为什么?
谢谢。
PS:请注意,我知道“圆和运算符=(圆& c)”而不是“圆运算符=(圆c)”和“返回*这个”而不是“返回圆(半径)”将使它变得正常。但我想了解我的代码中的行为和缺陷。谢谢。
发布于 2022-05-21 07:27:10

C++有几个特殊的成员函数,用于建造、分配和破坏。有规则,当这些规则是自动生成的。有些是精心挑选的,有些是有历史原因的。
在表的左边,如果您自己声明其中的一些,那么其他特殊成员函数会发生什么。对于特殊的成员函数,可以在类定义中编写实现,或者说= delete或= default。该表显示编译器隐式声明为默认值或已删除的其他哪一个。
在C++中有三/五/零的重要规则:您通常必须实现(或单独声明为已删除)特殊成员函数的三、五或零(默认构造函数除外):三 --这不是固定的规则,更多的是经验规则,如果没有遵循,通常会指向代码中的错误。
例如,如果您进行手动资源管理(例如打开文件或分配内存),则必须在复制和销毁期间处理。创建这两个move函数是可选的,并且可以添加性能改进(例如,只移动文件句柄而不是实际重新打开文件)。
特殊的成员函数有一个典型的签名,但您可以偏离它。自动生成的典型签名(和默认签名)如下:
T(); // default constructor
T(const T&); // copy constructor
T(T&&); // move constructor
T& operator=(const T&); // copy assignment
T& operator=(T&&); // move assignment
virtual ~T(); // destructor创建不同的签名可能会导致性能更差或功能意外,但它是完全合法的C++,有时还可以使用。
查看您在pastebin上发布的程序输出。
复制构造函数是自动生成的,在后台工作,您可以将它添加到控制台输出,以获得更完整的图片,正在发生的事情。
creating circle c1 with radius 10
object address-0x7ffc542431f0 and radius is 10
-> the circle(int) constructor is called with radius 10
creating empty circle c2
object address-0x7ffc54243200
-> the circle() default constructor is called, the radius is unspecified (as no default was set in the class definition nor in a member initialization list of the constructor)
creating empty circle c3
object address-0x7ffc54243210
-> the circle() default constructor is called, the radius is unspecified
c3=c2=c1
-> this acts like c3=(c2=c1), the right assignment is executed first
-> in the background the copy constructor for a new circle is called (as the parameter is a value and not a reference, so we have to create the parameter as separate object) and copies c1 into a new temporary circle, the new circle has address 0x7ffc54243220 and radius 10
my object is 0x7ffc54243200
my arg is 0x7ffc54243220
-> the copy assignment operator is called for c2 with the temporary circle object as parameter
object address-0x7ffc54243230 and radius is 20
-> the circle(int) constructor is called from within the assignment operator to create a temporary circle with radius 20 (`return circle(20)` line)
my object is 0x7ffc54243210
my arg is 0x7ffc54243230
-> the copy assignment operator is called on c3 with the temporary circle created in the previous return line
object address-0x7ffc54243240 and radius is 20
-> a new temporary circle is created, which is just discarded afterwards
in destructor for object 0x7ffc54243240
in destructor for object 0x7ffc54243230
in destructor for object 0x7ffc54243220
-> the temporary objects are destroyed
Final values
c1 radius-10
-> c1 was not modified after destruction
c2 radius-10
-> c2 got assigned the radius 10 from the temporary circle constructed from c1
c3 radius-20
-> c3 got assigned the radius 20 from the temporary circle constructed in the return from the (right) assignment operator
in destructor for object 0x7ffc54243210
in destructor for object 0x7ffc54243200
in destructor for object 0x7ffc542431f0
-> c1, c2 and c3 are destroyed at the end of main优化器可以删除许多用于创建临时对象的代码,如果类很简单,如果可以看到代码(如果循环在库中,则不工作),并且在选项中激活整个程序优化或链接时间优化(或者您的函数在类定义中被声明为内联或定义为内联函数,而不是单独的.cpp file=translation单元)。如果将控制台输出或其他非平凡代码放入特殊成员函数(优化器别无选择),则必须按顺序调用它们。
C++ Insight
使用C++ Insight (C++内部链接到您的代码),您可以输出大量编译器的自动代码生成:
inline,因为它们是在类定义中定义的,而不是单独的转换单元。circle(20)和用于创建参数的外部circle ):return circle(circle(20));这个双重创建被自动删除。它被称为复制省略(射影),它是标准要求的,在某些情况下,其中一个是返回一个值,当它可以直接使用时。// inline constexpr circle(const circle &) noexcept = default;。circle c2 = circle();。c1中的临时c1,因为副本赋值的参数是一个值,而不是引用:c3.operator=(c2.operator=(circle(c1)));。发布于 2022-05-21 08:03:23
如果你所说的“好”指的是“行为像一个毫无戒心的用户所期望的那样”,那么不,这是不对的。operator=通常通过引用返回*this是有原因的,而这个原因并不是效率。这是没有惊喜的。在C++中,我们期望用户定义的操作符应该以类似于内置操作符的方式运行。你的任务没有。
int x = 1, y = 2, z = 3;
(x = y) = z; // x == 3, y == 2, z == 3
circle p(1), q(2), r(3);
(p = q) = r; // actual : p.radius == 2, q.radius == 2, r.radius == 3
// expected : p.radius == 3, q.radius == 2, r.radius == 3当然,像(p = q) = r这样的东西在实际代码中是很少见的,但这只是一个测试用例,您的实现会默默地失败(即没有错误消息)。
这不是非法的,但这是意外的,你需要一个非常非常好的理由来产生一个组件来做一些意想不到的事情。据我所知,你没有。
人们可以并且确实修改赋值的结果,他们通常期望这些修改被应用到赋值对象,而不是一些会立即消失的临时的。
https://stackoverflow.com/questions/72255607
复制相似问题