pass by value -> copy -> copy constructor. Hence 2 copy constructors.
Ahh I get it, so the assignment operator calls the copy constructor. Interesting..
I think I call it a day with the constructor details here and move on with my studies.. I will try to follow the Rule of Three when writing code. That said, in the near future, I think I will be fine with the default copy constructor, assignment operator, and destructor. I will only have to specify my own constructors...
@seeplus, in your original code, if I comment out the assignment operator and add a simple assignment Joe2 = Joe at the end of your main, the copy constructor does not seem to be called.
Yet, in the previous case of Joe = person_double_age(Joe); which is also just an assignment, the copy constructor is called (twice, passing by value and assignment).
The argument is passed by value to your assignment operator which is why the copy constructor gets called.
The argument to the "default assignment operator" is passed by reference and therefore doesn't need the copy constructor to get called.
Change it to person& operator=(const person& per) and the copy constructor will no longer get called.
... but then you need to do assignments in the operator=() body - rather than swaps. The swap statements won't compile as per is now const so can't have it's content changed.
#include <iostream>
#include <string>
#include <utility>
class person {
public:
person(const std::string& name, int age);
person(const person& person_to_copy);
person& operator= (const person& rhs);
int get_age() const;
void set_age(int new_age);
std::string get_name() const;
void set_name(const std::string& new_name);
private:
std::string name;
int age {};
};
person::person(const std::string& new_name, int new_age) : name(new_name), age(new_age) {
std::cout << "custom constructor\n";
}
person::person(const person& person_to_copy) : person(person_to_copy.name, person_to_copy.age) {
std::cout << "copy constructor\n";
}
person& person::operator=(const person& rhs) {
name = rhs.name;
age = rhs.age;
std::cout << "copy assignment\n";
return *this;
}
int person::get_age() const {
return age;
}
void person::set_age(int new_age) {
age = new_age;
}
std::string person::get_name() const {
return name;
}
void person::set_name(const std::string& new_name) {
name = new_name;
}
person person_double_age(person someguy) {
someguy.set_age(someguy.get_age() * 2);
std::cout << "person_double_age\n";
return someguy;
}
int main() {
std::cout << "\nOriginal Joe with his age\n";
person Joe { "Joe", 14 };
std::cout << "\n Joe";
std::cout << "\n Joes name: " << Joe.get_name();
std::cout << "\n Joes age: " << Joe.get_age();
std::cout << "\n";
std::cout << "\nJoe with double age\n";
Joe = person_double_age(Joe);
std::cout << "\n name: " << Joe.get_name();
std::cout << "\n age: " << Joe.get_age();
std::cout << "\n";
std::cout << "\nNew Joe copy with double age\n";
person Joe2 { person_double_age(Joe) };
std::cout << "\n name: " << Joe2.get_name();
std::cout << "\n age: " << Joe2.get_age();
std::cout << "\n";
Joe2 = Joe;
}
Original Joe with his age
custom constructor
Joe
Joes name: Joe
Joes age: 14
Joe with double age
custom constructor // Called by copy ctor for someguy
copy constructor // Create parameter someguy
person_double_age
custom constructor // Called by copy ctor for retval
copy constructor // Create return value from someguy
copy assignment // Assign value returned from function into Joe
name: Joe
age: 28
New Joe copy with double age
custom constructor
copy constructor
person_double_age
custom constructor
copy constructor // Copy elision: the return value is created directly into Joe2
name: Joe
age: 56
copy assignment // Assign value of Joe into Joe2
It is interesting then that initialization seems to be treated differently under the hood than an assignment.
I would have guessed that
1 2 3
Joe = person_double_age(Joe);
person Joe2 { person_double_age(Joe) };
would both call the copy constructor twice (passing by value and return value) as well as an assignment. But in the latter case, the return value seems to be written instantly into the right spot, i.e. no copy assignment here.
Also, I have read about return-value optimization. Does it mean that some compilers will only call the copy constructor once in Joe = person_double_age(Joe), i.e. one copy constructor and one copy assignment?
An issue with copy assignment is that you'll probably also then need a move assignment - along with move constructor. If you just have one assignment (copy-swap) then you only need separate copy constructor and move constructor. The assignment will call the appropriate constructor as needed.