copy constructor and initialization

Pages: 12
seeplus wrote:

L32 - passed by value
L55 - passed by value

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...

Thanks for the many comments!
Last edited on
Ok, nevermind, I still want to know something.

@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).

Here the full code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#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 per);

	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 << "\ngeneral constructor called";
}

person::person(const person& person_to_copy) : person(person_to_copy.name, person_to_copy.age) {
	std::cout << "\ncopy constructor called";
}

// person& person::operator=(person per) {
// 	std::swap(per.age, age);
// 	std::swap(per.name, name);
// 	std::cout << "\nassignment copy called\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);
	return someguy;
}

int main() {
	std::cout << "\nOriginal Joe with his age";

	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";
	Joe = person_double_age(Joe);                   // calls copy constructor twice (i.e. default =() operator calls copy constructor?)
	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";

	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;  // why does this not call copy constructor?
}

The default assignment operator seems to act differently?
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.
Last edited on
... 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.

You're right. I missed the fact that the assignment operator, as written, used the copy-and-swap idiom.
Last edited on
Peter87 wrote:
The argument to the "default assignment operator" is passed by reference and therefore doesn't need the copy constructor to get called.


But why is the copy constructor called twice then in line 71? It's also just an assignment just like in line 84.
PhysicsIsFun wrote:
why is the copy constructor called twice then in line 71?
 
Joe = person_double_age(Joe);

The first time, to construct the by-value parameter someguy of person_double_age.
The second time, to construct the return value of person_double_age.
Last edited on
With copy assignment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#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
Last edited on
@Peter87, @keskiverto

thanks that makes it clear.

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?
Last edited on
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.
Last edited on
Topic archived. No new replies allowed.
Pages: 12