A copy constructor in C++ is a special constructor that creates a new object as a copy of an existing object of the same class. It is one of the most important parts of object behavior in C++ because copying happens in more places than beginners usually expect. A copy can happen when one object is initialized from another object, when an object is passed by value to a function, or when an object is returned by value in certain situations. If a class manages only simple built-in data, the default copy behavior may be enough. But if a class manages resources such as dynamic memory, file handles, or custom ownership, then copy behavior must be designed carefully.
This topic matters because copying is directly tied to correctness. A wrong copy can duplicate a pointer without duplicating the resource it points to, which can lead to double deletion, shared mutable state by accident, memory leaks, or undefined behavior. A good C++ programmer does not just know that copy constructors exist. A good C++ programmer understands when they are called, what problem they solve, and when copying should be allowed, customized, or prevented.
What Is a Copy Constructor in C++?
A copy constructor is a constructor that initializes one object by using another existing object of the same class. Its common form is shown below.
ClassName(const ClassName& other);
The argument is usually a constant reference because passing by value would itself require another copy, which would create an infinite loop of copy attempts. The reference also avoids unnecessary object duplication during the call.
A copy constructor in C++ creates a new object by copying the state of another object of the same class.
Syntax of Copy Constructor in C++
The usual syntax uses a constant reference parameter, though other forms are technically possible in some advanced cases. For normal class design, this is the form you should remember.
class Student
{
public:
Student(const Student& other)
{
// copy data from other
}
};
The constructor receives the source object and uses it to initialize the new object being created.
When the Copy Constructor Is Called
Many learners think the copy constructor is only used when they explicitly write a copy statement, but C++ can invoke it in several common situations.
- When one object is initialized from another object.
- When an object is passed by value to a function.
- When an object is returned by value from a function in situations where copying is not optimized away.
- When a temporary object is used to initialize another object.
Student s1;
Student s2 = s1; // copy constructor called
This is copy initialization. Even though it looks like assignment, it is actually object creation with copy semantics, not assignment to an already existing object.
Basic Example of Copy Constructor
#include <iostream>
using namespace std;
class Box
{
public:
int value;
Box(int v)
{
value = v;
}
Box(const Box& other)
{
value = other.value;
cout << "Copy constructor called" << endl;
}
};
int main()
{
Box b1(10);
Box b2 = b1;
cout << b2.value << endl;
}
Here b2 is created as a copy of b1. The copy constructor copies the value from the source object into the new object.
Compiler-Generated Copy Constructor
If you do not write a copy constructor, the compiler can generate one for you. This default copy constructor performs member-wise copying. That means each member of the source object is copied into the corresponding member of the new object.
For classes that contain only ordinary values such as int, double, or other classes with safe copy behavior, member-wise copying is often fine. But for classes that manage dynamic memory or ownership-sensitive resources, the default copy may be dangerous because it copies the pointer value, not the pointed-to resource.
| Case | Default Copy Usually Fine? | Reason |
|---|---|---|
| Only built-in data members | Yes | Simple values can be copied directly |
| Owns dynamic memory | No | Pointer address may be copied instead of data |
| Owns file or socket handle | No | Resource ownership may be duplicated incorrectly |
| Uses smart pointers with correct semantics | Depends | Copy behavior depends on the smart pointer type |
Shallow Copy vs Deep Copy
The biggest reason copy constructors become important is the difference between shallow copy and deep copy.
Shallow copy means copying member values directly. If a member is a pointer, then only the address is copied. Both objects may end up pointing to the same memory.
Deep copy means creating a separate resource for the new object and then copying the actual content into that new resource. This gives each object its own independent state.
Why Shallow Copy Can Be Dangerous
Consider a class that dynamically allocates memory with a raw pointer. If the compiler-generated copy constructor copies only the pointer address, then two objects will believe they own the same memory. When both destructors run, they may both try to delete the same memory block. That is undefined behavior.
class Sample
{
public:
int* data;
Sample(int value)
{
data = new int(value);
}
~Sample()
{
delete data;
}
};
If this class is copied with the default copy constructor, both objects will hold the same address inside data. That is exactly the kind of problem a custom copy constructor can solve.
Copy Constructor for Deep Copy
To perform a deep copy, the copy constructor must allocate fresh memory for the new object and copy the source value into that new memory.
#include <iostream>
using namespace std;
class Sample
{
public:
int* data;
Sample(int value)
{
data = new int(value);
}
Sample(const Sample& other)
{
data = new int(*other.data);
cout << "Deep copy constructor called" << endl;
}
~Sample()
{
delete data;
}
};
int main()
{
Sample s1(20);
Sample s2 = s1;
cout << *s1.data << " " << *s2.data << endl;
}
Now each object owns a separate memory block. Destroying one object does not damage the other object.
Copy Constructor and Pass by Value
Whenever you pass an object by value, C++ may create a copy of that object. That means the copy constructor can be invoked even when your code does not explicitly say “copy this object.”
void show(Box b)
{
cout << b.value << endl;
}
Calling show(b1); can copy b1 into the parameter b. This is one reason why heavy objects are often passed by const reference instead of by value. It avoids unnecessary copy operations.
Copy Constructor vs Copy Assignment Operator
These two are related but they are not the same thing.
| Feature | Copy Constructor | Copy Assignment Operator |
|---|---|---|
| Purpose | Creates a new object from an existing object | Copies data into an already existing object |
| Typical syntax | ClassName(const ClassName&) | ClassName& operator=(const ClassName&) |
| When used | During initialization | After objects already exist |
Box b1(5);
Box b2 = b1; // copy constructor
Box b3(0);
b3 = b1; // copy assignment operator
Confusing these two is a common beginner mistake. The first line creates and initializes a new object. The second modifies an object that already exists.
When a Copy Constructor Should Be Deleted
Not every class should be copyable. Some objects represent unique resources or exclusive ownership. In such cases, copying may be logically wrong or dangerous. C++ lets you disable copying explicitly.
class FileHandle
{
public:
FileHandle(const FileHandle&) = delete;
};
This makes the design intent clear. If someone tries to copy the object, the compiler reports an error instead of allowing unsafe behavior.
Copy Constructor and the Rule of Three
In classic C++ design, if a class needs a user-defined destructor, copy constructor, or copy assignment operator, it often needs all three. This idea is known as the Rule of Three. The reason is simple: classes that manage resources usually need consistent logic for destruction, copying during initialization, and copying into existing objects.
In modern C++, this idea extends to the Rule of Five when move operations are also considered. Even if you later use smart pointers and RAII to avoid much of this manual work, the Rule of Three remains important for understanding why copy constructors matter.
Defaulted Copy Constructor in C++
Modern C++ also lets you explicitly request the compiler-generated copy constructor with = default. This is useful when you want normal copy behavior and also want the intent to be visible in the class definition. It tells readers and the compiler that copying is allowed and the standard member-wise behavior is acceptable for that class.
class Point
{
public:
int x;
int y;
Point(const Point&) = default;
};
Copy Constructor and Member Initialization
In well-designed classes, copying often happens through a member initializer list instead of assigning values inside the constructor body. This is especially important for const members, reference members, and class-type members that should be initialized directly rather than default-constructed first and assigned later.
class Number
{
public:
int value;
Number(const Number& other) : value(other.value)
{
}
};
This style is often cleaner and can be more efficient. It also matches how constructors are intended to work in C++: initialize members properly at construction time instead of constructing them first and then assigning new values afterward.
Best Practices for Copy Constructors in C++
- Use a
const ClassName¶meter in normal copy constructor design. - Let the compiler-generated copy constructor work only when member-wise copying is truly safe.
- Write a custom copy constructor for classes that own raw resources.
- Delete the copy constructor when copying should not be allowed.
- Prefer RAII and standard library types to reduce manual copy-management complexity.
These rules help keep ownership and object behavior predictable. Predictability is a major part of writing reliable C++ code.
Important Rules to Remember About Copy Constructor
- A copy constructor creates a new object from an existing object of the same class.
- It is commonly declared as
ClassName(const ClassName& other). - It can be called during initialization, pass by value, and return by value.
- Default member-wise copying is not always safe for resource-owning classes.
- Deep copy is needed when objects must own separate copies of resources.
Continue learning C++ in order
Follow the topic sequence with the previous and next lesson.