How (or why) the copy constructor is called? [duplicate] - c++

This question already has answers here:
What's the difference between assignment operator and copy constructor?
(8 answers)
Closed 15 days ago.
#include <iostream>
using namespace std;
struct Car
{
int size = 4;
int* tires = nullptr;
Car()
{
cout << "ctor" << endl;
tires = new int[size];
}
Car(const Car& car)
{
cout << "copy ctor" << endl;
tires = new int[size];
memcpy(tires, car.tires, size*sizeof(int));
}
Car& operator= (Car& car)
{
cout << "copy assignment operator" << endl;
tires = new int[size];
memcpy(tires, car.tires, size*sizeof(int));
return *this;
}
~Car()
{
cout << "dtor" << endl;
delete tires;
}
};
int main()
{
cout << "starting..." << endl;
Car car1;
car1.tires[0] = 2;
// Car car2(car1); // #1
Car car2 = car1; // #2 I was expecting the assingment operator to be called here
cout << car1.tires[0] << " " << car2.tires[0] << endl;
return 0;
}
I understand why the copy constructor is called at #1, but how does it end up being called at #2?

The statement:
Car car2 = car1;
is NOT assignment, like you are thinking. Assignment can happen only on an existing object, not when an object is being created.
The statement above is actually initialization instead, specifically Copy Initialization:
Syntax
T object = other; (1)
...
The effects of copy initialization are:
...
Otherwise, if T is a class type and the cv-unqualified version of the type of other is T or a class derived from T, the non-explicit constructors of T are examined and the best match is selected by overload resolution. That constructor is then called to initialize the object.
...
So, in your case, the compiler finds the copy constructor is a match and treats the statement as-if you had written Car car2(car1); instead.
That is why the copy constructor is called.

A C++ compiler will call the assignment operator only for an object that already exists, to overwrite the values of this object with the values of another instance.
car car1,car2;
car2=car1;
And it will call the copy constructor in the below cases:
if the object is initialized with another instance.
car car1;
car car2=car1; //This is equivalent to car2(car1);
if an object is passed to a function as a non-reference parameter.
Here you can see the parameter of func, where the obj2 is being initialized and not assigned.
void func(car obj2)//not a reference parameter(car& obj2)
{
//
}
car obj1;
func(obj1);
An object is returned from a function - I don't have an idea about this yet.
Initialization is when a new object or variable is being created to hold a value, and assignment is when an existing object or variable is set to hold a new value.
For the above assignment operator example, both the car1 and car2 objects hold a value (probably garbage) at the time of creation, and then are set to a new value.

Related

Why is the copy constructor called in this example with std::vector?

#include <iostream>
#include <vector>
using namespace std;
// Move Class
class Move {
private:
// Declare the raw pointer as
// the data member of class
int* data;
public:
// Constructor
Move(int d)
{
// Declare object in the heap
data = new int;
*data = d;
cout << "Constructor is called for "
<< d << endl;
};
// Copy Constructor
Move(const Move& source)
: Move{ *source.data }
{
cout << "Copy Constructor is called -"
<< "Deep copy for "
<< *source.data
<< endl;
}
// Move Constructor
Move(Move&& source)
: data{ source.data }
{
cout << "Move Constructor for "
<< *source.data << endl;
source.data = nullptr;
}
// Destructor
~Move()
{
if (data != nullptr)
cout << "Destructor is called for "
<< *data << endl;
else
cout << "Destructor is called"
<< " for nullptr "
<< endl;
delete data;
}
};
// Driver Code
int main()
{
// Vector of Move Class
vector<Move> vec;
// Inserting Object of Move Class
vec.push_back(Move{ 10 });
vec.push_back(Move{ 20 });
return 0;
}
output:
Constructor is called for 10
Move Constructor for 10
Destructor is called for nullptr
Constructor is called for 20
Move Constructor for 20
Constructor is called for 10
Copy Constructor is called -Deep copy for 10
Destructor is called for 10
Destructor is called for nullptr
Destructor is called for 10
Destructor is called for 20
I think what you are asking is why is the copy constructor called instead of your move constructor? It is because your move constructor is not noexcept. If you mark it as no except, the move constructor will be used during reallocation instead.
See this related answer for more information: https://stackoverflow.com/a/47017784/6324364
The common recommendation is that move constructors should be noexcept if at all possible. Many standard library functions will not use the move constructor if it could throw an exception.
If your question is instead about the order of constructor calls - that is just dependent on the implementation. An example implementation of push_back could be:
// The callsite of this function is where "Constructor is called for 20"
template<typename T>
void push_back(T&& value) {
// If the vector is full resize
if (size() == capacity()) {
auto* oldBuffer = m_buffer;
// allocate larger amount of space,
// increasing capacity but keeping size the same
// ...
// Here is your move constructor call
m_buffer[size()] = std::move(value);
// copy/move over old elements. This is your copy constructor
// If your move constructor was noexcept, it would be used.
// Otherwise the copy constructor is called.
// ...
m_size++;
// deallocate old buffer
// ...
} else {
m_buffer[size() - 1] = std::move(value);
m_size++;
}
}
The reason you seemingly get two constructor calls for 10 after the move constructor for 20 is because that is what your code does:
// Constructor
Move(int d)
{
// Declare object in the heap
data = new int;
*data = d;
cout << "Constructor is called for "
<< d << endl;
};
// Copy Constructor
Move(const Move& source)
// Here, your Copy constructor is calling the other constructor
: Move{ *source.data }
{
cout << "Copy Constructor is called -"
<< "Deep copy for "
<< *source.data
<< endl;
}
In your copy constructor you are explicitly calling your other constructor leading to the two log messages.
The implementation may do things in this order (move/copy new element, then move/copy old elements) as part of the strong exception guarantee provided by vector which says that the vector state will not be corrupted if copy/move of the new items fails. If it moved the old items first and then the new item constructor threw an exception, the vector state would be corrupted.
If you can add what you expected to happen in the code, maybe someone can give a better answer. As it stands, I don't fully understand what you are asking. You get those calls because that is how it is implemented.
Vector is stored as a one piece in memory, continously. When you add the second element to the vector, its current memory needs to be expanded to accomodate the newly added element. This will require your already existing elements to be copied somewhere else where enough memory is allocated to accomodate both (in your case) the first and second element. Take a look at what reserve is doing and how you can benefit from it. Or, similarly, if your vector is of fixed size, you may also take a look at array.

Is Assignment Operator or Copy Constructor invoked for array/vector insertions?

Language: C++
My question is about whether the copy constructor or the assignment operator is called in the following situations. To precede, I understand the following:
MyClass a(3); // single param constructor
MyClass b(a); // copy constructor invoked
MyClass c = b; // copy constructor invoked
MyClass d; // default constructor
d = c; // assignment operator invoked
However, I was hoping someone could give a similar breakdown for these:
1) For lines 2-3, is assignment operator or copy constructor called?
MyClass arr[10];
arr[2] = a;
arr[5] = MyClass(1);
2) Constructor, then copy constructor? Or constructor, then assignment operator?
MyClass arr2[] = {MyClass(), MyClass(9)};
3) Assuming vector v's internal representation has space for one more object, is new element copied using assignment operator or copy constructor?
std::vector<MyClass> v;
v.push_back(MyClass(2));
...
...
4) Assuming vector v's internal representation is out of space and must realloc, are old elements copied using assignment operator, or copy constructor?
v.push_back(MyClass(2)); // Assuming vector is out of space; must realloc
MyClass arr[10];
Constructor for MyClass is called 10 times, as 10 objects of arr are created.
arr[2] = a;
Assignment operator is invoked, assigns arr[2] to a.
arr[5] = MyClass(1);
First single param constructor with parameter 1 is called and creates an object of MyClass. Then assignment operator is invoked.
MyClass arr2[] = {MyClass(), MyClass(9)};
Two constructors and only are called here. First the Myclass() and then "single param constructor" MyClass(9). The array declaration with initialization is not an assignment, cause no existing arrays members exists to assign to.
std::vector<MyClass> v;
Constructor for std::vector<MyClass> is called.
v.push_back(MyClass(2));
std::vector::push_back creates a copy of the class and stores it. So first MyClass(2) constructor is called, then copy constructor MyClass(const MyClass &) is called to copy the value. Then the copied object is stored.
Assuming vector v's internal representation is out of space and must realloc, are old elements copied using assignment operator, or copy constructor?
Copy operator is invoked for each member. So:
std::vector<MyClass> a;
Calls constructor for std::vector<MyClass>
a.push_back(MyClass(1));
Calls constructor for MyClass(1) and copies it using copy cosntructor MyClass(MyClass&).
After that if we add another element to the array:
a.push_back(MyClass(2));
Then MyClass(2) constructor is called, then copy constructor is called for MyClass(MyClass&) for the just constructed MyClass(2) object. Then the vector copy-constructs all existing members from the ned, so for the pre-existing object in the vector MyClass(1) already upped copy constructor is called.
Really, play with it a little. And insert cout everywhere to see it:
struct MyClass {
MyClass(int a = 0) : _a(a) {
_count = count++;
std::cout << __func__ << " "
<< _count << " "
<< this << " "
<< this->_a << "\n";
}
MyClass(const MyClass &old_obj) {
this->_a = old_obj._a;
std::cout << __func__ << "Copy "
<< _count << " "
<< this << " "
<< this->_a << " "
<< &old_obj << " "
<< old_obj._a << "\n";
}
void operator=(const MyClass &old_obj) {
this->_a = old_obj._a;
std::cout << "MyClass" << __func__ << " "
<< _count << " "
<< this << " "
<< this->_a << " "
<< &old_obj << " "
<< old_obj._a << "\n";
}
static int count;
int _count;
int _a;
};
int MyClass::count = 0;
When you have
type variable_name = some_value
then you are declaring a variable and will always be calling its constructor (if it has one). This is called copy initialization and this will never be assignment
So, in
MyClass arr[10]; // 1
arr[2] = a; // 2
arr[5] = MyClass(1); // 3
Line 1 creates an array of 10 MyClass and default constructs each of them. Lines 2 and 3 are assignment.
In
MyClass arr2[] = {MyClass(), MyClass(9)};
You initialize an array of 2 objects using the values in the *braced-init-list` as the initializers for the array members. There are many rules governing list initialization but the one thing they have in common is no assignment will happen, only constructor calls.
With
std::vector<MyClass> v;
v.push_back(MyClass(2));
Assuming the vector doesn't reallocate, you have a constructor call for MyClass(2) and then the element in the vector is copy constructed from that temporary object. If the vector has to grow then all of the current elements are copy/move constructed to a new buffer then the temporary is copied constructed at the end.

Copy constructor and dynamic memory

I am a beginner in programming and I am learning about copy constructors. From different sources I can see that copy constructors are useful if I want to "deep copy" a class object so the new object's pointer members will point to new memory locations.
My question is, what is the advantage of defining the copy constructor as I do in class CopyCat in my example, if I get the same result with an empty copy constructor (as in class EmptyCat)?
My second question is, why do class Cat and class EmptyCat work differently? The only difference between them is that I define an empty copy constructor in EmptyCat. But as I run the program I can see that in EmptyCat after the copying the pointer member points to a new location while in class Cat it works as a shallow copy.
#include "iostream"
class Cat
{
public:
void GetMem() { std::cout << itsAge << "\n"; }
private:
int * itsAge = new int;
};
class EmptyCat
{
public:
EmptyCat() {}
~EmptyCat() {}
EmptyCat(EmptyCat&obj) {}
void GetMem() { std::cout << itsAge << "\n"; }
private:
int * itsAge = new int;
};
class CopyCat
{
public:
CopyCat() {}
~CopyCat() {}
CopyCat(CopyCat&obj);
int GetAge() { return *itsAge; }
void GetMem() { std::cout << itsAge << "\n"; }
private:
int * itsAge = new int;
};
CopyCat::CopyCat(CopyCat & obj)
{
itsAge = new int;
*itsAge = obj.GetAge();
}
int main()
{
Cat Garfield;
Cat Kitty(Garfield);
std::cout << "Memory addresses for the objects' <itsAge> member:" << std::endl;
std::cout << "Garfield and Kitty (Class Cat):" << std::endl;
Garfield.GetMem();
Kitty.GetMem();
EmptyCat Meow;
EmptyCat Purr(Meow);
std::cout << std::endl << "Meow and Purr (Class EmptyCat):" << std::endl;
Meow.GetMem();
Purr.GetMem();
CopyCat Fluffy;
CopyCat Felix(Fluffy);
std::cout << std::endl << "Fluffy and Felix (Class CopyCat):" << std::endl;
Fluffy.GetMem();
Felix.GetMem();
system("pause");
return 0;
}
If I run the program I get this:
Memory addresses for the objects' <itsAge> member:
Garfield and Kitty (Class Cat):
00BBDA60
00BBDA60
Meow and Purr (Class EmptyCat):
00BB46A0
00BB8280
Fluffy and Felix (Class CopyCat):
00BB82B0
00BBE8A0
Press any key to continue . . .
Deep copying and shallow copying is rather a C concept, where you have only structures and raw pointers. A pointer can be owned, in which case the copy must be deep, or it can be shared, in which case the copy is shallow (and you have to be careful about freeing it if it's allocated with malloc).
In C++, new is now effectively deprecated. We have unique pointers, which are "owning pointers" and "shared pointers". However pointers are relatively rare. Array members of classes are std::vectors, string members are std::strings. And copies are automatically deep, (You use a reference if you want a shallow copy).
Pointers are held back for relatively unusual situations, like trees and graphs.
My question is, what is the advantage of defining the copy constructor as I do in class CopyCat in my example, if I get the same result with an empty copy constructor (as in class EmptyCat)?
You don't get the same result. CopyCat allocates new memory and copies the value from the old class. The EmptyCat just allocates new memory, but does not copy the value.
My second question is, why do class Cat and class EmptyCat work differently? The only difference between them is that I define an empty copy constructor in EmptyCat. But as I run the program I can see that in EmptyCat after the copying the pointer member points to a new location while in class Cat it works as a shallow copy.
In Cat you haven't declared a copy constructor, so the compiler will generate one when needed. The default copy constructor does a member-wise copy from the original. In your case, this will copy the pointer (so that it stores the same address as the original).
In the EmptyCat you have a user defined copy constructor. But as that one doesn't handle the pointer member, its default value will be used.
int * itsAge = new int;
This is what allocates a new int and gets you a different pointer value.
You are not getting the same behavior with and without an empty copy constructor. EmptyCat(EmptyCat& obj) { } does absolutely nothing.
CopyCat(CopyCat& obj) {
itsAge = new int;
*itsAge = obj.GetAge();
}
dynamically allocates a new int and assigns to it a value from the obj.

How this code runs without overloading for the assignment operator

I wonder how this code run specifically line 54 (line2 = line1) although there is no overloading for the assignment operator ?
It seems from the output that neither the copy constructor nor the normal constructor were called and surprisingly it gets output as expected 199 199
#include <iostream>
using namespace std;
class Line
{
public:
int getLength();
Line( int len ); // simple constructor
Line( const Line &obj); // copy constructor
~Line(); // destructor
private:
int *ptr;
};
Line::Line(int len)
{
cout << "Normal constructor allocating ptr" << endl;
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "Copy constructor allocating ptr." << endl;
ptr = new int;
*ptr = *obj.ptr;
}
Line::~Line(void)
{
cout << "Freeing memory!" << endl;
delete ptr;
}
int Line::getLength()
{
return *ptr;
}
void display(Line obj)
{
cout << "Length of line : " << obj.getLength() <<endl;
}
// Main function for the program
int main()
{
Line line1(199);
Line line2(1);
line2 = line1; // How this is executed ??!
cout << line1.getLength() << " " << line2.getLength() << endl ;
/*display(line1);
display(line2);*/
cin.get();
return 0;
}
What you have there is undefined behavior. You assign line2 = line1 but have no user-defined assignment operator, so you use the default one provided by the compiler. And the default one simply copies all the fields, which in your case includes an int*. That gives you two copies of the same int*, leaks the value that line2 previously pointed to, and eventually double-deletes the one line1 originally pointed to. The second delete of the same pointer, which occurs when line1 goes out of scope at the end of main(), invokes undefined behavior.
If you have a destructor which frees resources, you probably need an assignment operator too. See the Rule of Three: http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29
But the best solution is to stop using raw pointers. Use a smart pointer and this problem will not occur in the first place, and you can omit your destructor.
In a situation like this, writing your own copy constructor, assignment operator and destructor should be your last resort, not your first reaction.
Your first reaction should usually be to use some pre-defined class that already handles these chores for you. In this case, changing from a raw pointer to a shared_ptr (for only one possibility) cleans up the code fairly quickly. Using it, the code ends up something like this:
#include <iostream>
#include <memory>
using namespace std;
class Line
{
public:
int getLength();
Line( int len ); // simple constructor
~Line(); // destructor
// copy constructor removed, because the one supplied by the
// compiler will be fine. Likewise the compiler-generated assignment
// operator.
private:
shared_ptr<int> ptr;
};
Line::Line(int len)
{
cout << "Normal constructor allocating ptr" << endl;
// Note the use of make_shared instead of a raw `new`
ptr = make_shared<int>(len);
}
Line::~Line(void)
{
cout << "Freeing memory!" << endl;
// don't need to actually do anything--freeing is automatic
}
int Line::getLength()
{
return *ptr;
}
void display(Line obj)
{
cout << "Length of line : " << obj.getLength() <<endl;
}
// Main function for the program
int main()
{
Line line1(199);
Line line2(1);
line2 = line1; // uses compiler-generated assignment operator (which works)
cout << line1.getLength() << " " << line2.getLength() << endl ;
display(line1);
display(line2);
cin.get();
return 0;
}
Depending upon the situation, a unique_ptr might be a better fit than a shared_ptr. In this case, shared_ptr is probably easier to incorporate into the existing code though.
You might also want to read R. Martinho Fernandes' Rule of Zero blog post on this subject.
If no user-defined copy assignment operators are provided for a class type (struct, class, or union), the compiler will always declare one as an inline public member of the class.
This implicitly-declared copy assignment operator has the form T& T::operator=(const T&) if all of the following is true:
Each direct base B of T has a copy assignment operator whose parameters are B or const B& or const volatile B&
Each non-static data member M of T of class type or array of class type has a copy assignment operator whose parameters are M or const M& or const volatile M&
Otherwise the implicitly-declared copy assignment operator is declared as T& T::operator=(T&). (Note that due to these rules, the implicitly-declared copy assignment operator cannot bind to a volatile lvalue argument)
Copied from this article from CPPReference.

Constructor calls during assignment operations and object passing

I am confused over the differences between passing objects by reference and by value to functions of a particular class. If I pass objects by value, I know that the default copy constructor makes a member-by-member copy of the the object for use in the given function. However, if I am passing objects as a const reference for a class that requires deep copy, is the copy constructor still called? Say that I had a function
void debug(const MyClass& object1);
Would passing object1 call the copy constructor? Or is the object passed into the function directly without having a copy made? One more question - If I have a class called Fraction-
Fraction A(1,2); // 1 is this numerator, 2 the denominator
A = Fraction(2,3);
Does the aforementioned line call the default constructor to make a temporary object Fraction(2,3) and then the assignment operator?
Thanks.
Would passing object1 call the copy constructor?
No, it will not call the copy constructor since passed by reference
No copy is made in this case
A = Fraction(2,3);
Yes, it will call the constructor with two parameters (or default constructor if both parameters have default values), then call the copy assignment operator.
You can see the output from code below:
#include <iostream>
using namespace std;
class Fraction
{
public:
int denom;
int nominator;
Fraction(int d , int n ):denom(d), nominator(n)
{
cout << "call non-copy constructor" <<endl;
}
Fraction(const Fraction& rhs)
{
cout << "call copy constructor" <<endl;
denom = rhs.denom;
nominator = rhs.nominator;
}
const Fraction& operator=(const Fraction& rhs)
{
cout << "call copy assignment operator" << endl;
if (this == &rhs)
{
return *this;
}
denom = rhs.denom;
nominator = rhs.nominator;
return *this;
}
};
void debug(const Fraction& obj)
{
cout << "this is debug: pass by reference " <<endl;
}
void debugPassByValue(Fraction obj)
{
cout << "this is debug: pass by value" <<endl;
}
int main()
{
Fraction A(1,2);
cout << "--------------" <<endl;
debug(A);
cout << "--------------" <<endl;
A = Fraction(2,3);
cout << "--------------" <<endl;
debugPassByValue(A);
cout << "--------------" <<endl;
cin.get();
return 0;
}
You will see the following output:
call non-copy constructor //Fraction A(1,2);
--------------
this is debug: pass by reference //debug(A);
--------------
call non-copy constructor //A = Fraction(2,3);---> construct temporary object
call copy assignment operator //A = Fraction(2,3);
--------------
call copy constructor //debugPassByValue(A);
this is debug: pass by value
--------------
Now you will have a clearer view of what are called.
In the following we will consider [x] to mean that x is optional.
I am confused over the differences between passing objects by reference and by value to functions of a particular class.
When you pass an object by value, the program must create the local object of the function therefore it calls the copy constructor of the class to create this object. When you pass by reference (and seemingly by pointer) the variable object1 inside the function is just an alias of the object you passed to the function; therefore if you edit the one inside the function, the edits will be applied to the outside object as well.
Does the aforementioned line call the default constructor to make a temporary object Fraction(2,3) and then the assignment operator?
It's the assignment operator. Considering A to be an already declared variable of type X it will be called X Fraction::operator=([const] Fraction[&]) or any compatible type.
Notice: when declaring Fraction x = Fraction(2, 3) it won't be used the operator= as you may expect, the corresponding constructor will be called instead (in this case Fraction::Fraction([const] Fraction[&])).
Indeed in the case of debug no copy is made.
In the second case, I'm not quite sure I understand your question. This line:
A = Fraction(2,3);
Should use Fraction's assignment operator. A already exists, so it uses the assignment operator on instance A to assign it Fraction(2,3) which is a temporary object.