
C++ has several special member functions for construction, assignment and destruction. There are rules, when those are auto-generated. Some are carefully chosen, some have historical reasons.
On the left side of the table you see, what happens to the other special member functions, if you declare some of those yourself. For the special member functions you can write an implementation or say = deleteor = default in the class definition. The table shows, which of the other ones the compiler implicitly declares as defaulted or as deleted.
There is the important rule of three/five/zero in C++: You typically have to either implement (or individually declare as deleted) three, five or zero of the special member functions (except the default constructor): https://en.cppreference.com/w/cpp/language/rule_of_three
This is no fixed rule, more a rule of thumb, often pointing to bugs in the code, if it is not followed.
E.g. if you do manual resource management (e.g. opening files or allocating memory), then this has to be handled during copy as well as during destruction. Creating the two move functions is optional and can add a performance improvement (e.g. by just moving the file handle instead of actually reopening files).
The special member functions have a typical signature, but you can deviate from it. The typical (and default for auto-generated) signatures are:
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
Creating a different signature can lead to worse performance or unexpected functionality, but it is perfectly legal C++ and can have uses sometimes.
Going through the program output you posted on pastebin
The copy constructor is auto-generated and works in the background, you could add it with console output to get an even more complete picture, what is happening.
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
The optimizer can remove a lot of code for creating temporary objects, if the classes are simple, if it can see the code (does not work, if circle is in a library) and whole program optimization or link time optimization is activated in the options (or your functions are declared as inline or defined in your class definition as is the case with your code instead of a separate .cpp file=translation unit). If you put console output or other non-trivial code in the special member functions - the optimizer has no choice - they have to be called in order.
C++ Insights
With C++ Insights (C++ Insights Link to your code) you can output a lot of the automatic code generation of the compiler:
The member function are all declared as inline as they are defined in your class definition instead of a separate translation unit.
In line 31 the generation of the temporary circle is wrapped into another copy constructor (the inner circle(20) as written in your code and the outer circle for creation of the parameter): return circle(circle(20)); This double creation is removed automatically. It is called copy elision (https://en.cppreference.com/w/cpp/language/copy_elision), which is mandated by the standard in some situations, one of those is returning a value, when it can be directly used.
In line 34 the compiler auto-generates the default copy constructor // inline constexpr circle(const circle &) noexcept = default;
In lines 43 and 45 you see the default constructor being called circle c2 = circle();
In line 47 you see the parantheses being added and the temporary circle out of c1 being created, as the parameter of the copy assignment is a value instead of a reference: c3.operator=(c2.operator=(circle(c1)));
circle& operator = (const circle& c) { ... ; return *this; }?0xffffcbbcand0xfffffbf8are temporary objects, if I well understand, what disturbs you is that you see only two temporary object addresses instead of four ?