A copy constructor is a constructor which gets invoked when we instantiate the class type with object of same type. For example,
#include <iostream>
class Point
{
int m_x{};
int m_y{};
public:
Point() = default;
Point(int x, int y)
: m_x{x}, m_y{y}
{
std::cout << "Constructing using Point(" << x << ", " << y << ")\n";
}
// copy constructor
Point(const Point &obj)
: m_x{obj.m_x}, m_y{obj.m_y}
{
std::cout << "Using copy constructor Point(const Point&)\n";
}
void print() const
{
std::cout << "{" << m_x << ", " << m_y << "}\n";
}
};
int main(int argc, char const *argv[])
{
Point x{1, 2};
Point p{x};
return 0;
}Here Point(const Point &obj) is the copy constructor. When we initialize p with x, this constructor gets invoked and does the member wise copy of data members.
Please note that we are passing argument by const reference for surety that constructor does not modify the argument in the body.
If we run this program, we should see following output (on my machine, maybe different on yours):
Constructing using Point(1, 2)
Using copy constructor Point(const Point&)
So, we see that copy constructor is being invoked when we pass existing object for initialization.
Note
The result may be different on your machine because compiler may optimize copy constructor for certain cases. Additionally, you should not add any behavior to the copy constructor because of this optimization as that behavior may or may not be executed. This optimization is called copy elision.
Implicit copy constructor
If we do not define a copy constructor, compiler uses implicit copy constructor. Implicit copy constructor does the same thing as our copy constructor does. So, unless there is some requirement, it is recommended to use implicit copy constructor.
Passing class type by value calls copy operator
When we pass a class type by value to a function, the copy constructor gets called to copy the argument to the parameter. For example,
#include <iostream>
class Point
{
int m_x{};
int m_y{};
public:
Point() = default;
Point(int x, int y)
: m_x{x}, m_y{y}
{
std::cout << "Constructing using Point(" << x << ", " << y << ")\n";
}
// copy constructor
Point(const Point &obj)
: m_x{obj.m_x}, m_y{obj.m_y}
{
std::cout << "Using copy constructor Point(const Point&)\n";
}
void print() const
{
std::cout << "{" << m_x << ", " << m_y << "}\n";
}
};
// pass by value should call Point copy constructor to create copy of argument.
void printPoint(Point p)
{
p.print();
}
int main(int argc, char const *argv[])
{
Point x{1, 2};
printPoint(x);
return 0;
}When we run the program, we should see the following output(again on my machine, maybe different on yours):
Constructing using Point(1, 2)
Using copy constructor Point(const Point&)
{1, 2}
So, this time the copy constructor is getting called because we are passing x by value to function printPoint.
Returning class object by value calls copy constructor
As we know that returning by value creates a temporary object by initializing it by the value to be returned. Then this temporary object gets returned back to the caller.
So, when type of class object is same as return type, the temporary object is initialized with the class object by implicitly invoking the copy constructor of the class. For example,
#include <iostream>
class Point
{
int m_x{};
int m_y{};
public:
Point() = default;
Point(int x, int y)
: m_x{x}, m_y{y}
{
std::cout << "Constructing using Point(" << x << ", " << y << ")\n";
}
// copy constructor
Point(const Point &obj)
: m_x{obj.m_x}, m_y{obj.m_y}
{
std::cout << "Using copy constructor Point(const Point&)\n";
}
void print() const
{
std::cout << "{" << m_x << ", " << m_y << "}\n";
}
};
Point createPoint(int a, int b)
{
Point x{a, b};
return x;
}
int main(int argc, char const *argv[])
{
Point y{createPoint(1, 2)};
return 0;
}Running the program should print the std::cout of copy constructor twice.
Note
It did not print the
std::couteven at least once on my machine which I doubt maybe compiler optimized copy constructor (copy elision)
Last but not least, similar to default constructor, we can explicitly request compiler to generate a copy constructor by using =default. For example,
// copy constructor
Point(const Point &obj) = default;