In this lesson, you will learn
A copy constructor takes a reference to an object of the same class as an argument:
ClassName(const ClassName &obj)
{
member1 = obj.member1;
...
//Other Members
}
Here, obj is a reference to the object that is being copied. The const qualifier is optional, but is added to prevent accidental modification of the object.
The copy constructor is invoked using the following syntax.
Method-1:
Class-Name p2(p1); An object p2 is initialized by the value of p1.
Method-2:
Class-Name p2 = p1;
Note: A copy constructor takes a reference to an object of the same class as an argument.
#include<iostream>
#include<string>
using namespace std;
class Book {
private:
string title;
string author;
int pages;
public:
// Parameterized Constructor
Book(string t, string a, int p) : title(t), author(a), pages(p) {}
// Copy Constructor
Book(const Book &b) {
title = b.title;
author = b.author;
pages = b.pages;
cout << "Copy constructor called for Book: " << title << endl;
}
// Function to display book details
void display() const {
cout << "Title: " << title << ", Author: " << author
<< ", Pages: " << pages << endl;
}
};
int main() {
// Create a Book object using the parameterized constructor
Book book1("1984", "George Orwell", 328);
// Use the copy constructor to create a new Book object
Book book2 = book1; // Copy constructor is called here
// Display details of both books
cout << "Book 1 Details:" << endl;
book1.display();
cout << "Book 2 Details:" << endl;
book2.display();
return 0;
}
Copy constructor called for Book: 1984
Book 1 Details:
Title: 1984, Author: George Orwell, Pages: 328
Book 2 Details:
Title: 1984, Author: George Orwell, Pages: 328
If you do not provide the user-defined copy constructor, then the compiler creates it automatically.
Let’s see the following examples where we are not adding a user-defined copy constructor in the class Box.
If you don’t define a copy constructor, the compiler provides a default one, which performs a shallow copy of the object.
Let’s see this with an example:
#include<iostream>
using namespace std;
class Box {
public:
int length;
Box(int l) : length(l) {}
};
int main() {
Box box1(10); // Create an object
Box box2 = box1; // Copy constructor called
cout << "Box1 length: " << box1.length <<&box1<< endl;
cout << "Box2 length: " << box2.length <<&box2 << endl;
return 0;
}
Box1 length: 10
Box2 length: 10
In this example, box2 is created as a copy of box1, and the default copy constructor simply copies the value of length.
Shallow copy issues happen only when you have pointers or dynamically allocated memory inside your class.
Since Box has no pointers (only an int), the concept of shallow vs. deep doesn’t really apply here.
Technically, this is just a member-wise copy — and it behaves like a deep copy because int is a value type.
int* length;
then the default/your copy constructor as written would be a shallow copy, and you’d need to write a deep copy constructor to allocate new memory.
If you don’t define your own copy constructor, the compiler provides a default one, which performs a shallow copy of the object(In case of a pointer variable only). Sometimes, the default shallow copy isn’t enough, especially if the class manages dynamic memory.
In such cases, you need to define a custom copy constructor.
However, we need to define our own copy constructor only when your class or data structure handles dynamically allocated resources, such as memory, file handles, or network connections, and it is known as the deep copy in C++.
A shallow copy creates a new object but does not allocate new memory for the resources the object manages.
Shallow Copy means that only the pointers will be copied, not the actual resources that the pointers are pointing to. This can lead to dangling pointers if the original object is deleted.
#include<iostream>
using namespace std;
class ShallowCopyExample {
private:
int* data;
public:
// Constructor
ShallowCopyExample(int value) {
data = new int(value); // Allocate memory and initialize
cout << "Constructor: Memory allocated at " << data << endl;
}
// Shallow Copy Constructor
ShallowCopyExample(const ShallowCopyExample &obj) {
data = obj.data; // Shallow copy: copy the pointer, not the data
cout << "Copy Constructor (Shallow): Pointer copied, address " << data << endl;
}
// Destructor
~ShallowCopyExample() {
cout << "Destructor: Memory freed at " << data << endl;
delete data; // Free the allocated memory
}
// Function to display the value and its memory address
void display() const {
cout << "Value: " << *data << ", Address: " << data << endl;
}
};
int main() {
ShallowCopyExample obj1(100); // Constructor is called
ShallowCopyExample obj2 = obj1; // Shallow copy constructor is called
// Display values and their addresses
cout << "obj1: ";
obj1.display();
cout << "obj2: ";
obj2.display();
return 0;
}
Constructor: Memory allocated at 0x21d8ce13860
Copy Constructor (Shallow): Pointer copied, address 0x21d8ce13860
obj1: Value: 100, Address: 0x21d8ce13860
obj2: Value: 100, Address: 0x21d8ce13860
Destructor: Memory freed at 0x21d8ce13860
Destructor: Memory freed at 0x21d8ce13860
Memory Addresses:
obj1.obj2, indicating that both obj1 and obj2 share the same memory location.display function confirms that both obj1 and obj2 have the same memory address for their data members.Destructor Output:
obj1 and obj2 share the same memory. This can cause undefined behavior, including potential crashes.A deep copy creates a new object and allocates new memory for the resources. It copies the actual data from the original object to the new object. Thus, each object has its own separate memory addresses for its resources.
Deep copy is possible only with a user-defined copy constructor. In a user-defined copy constructor, we make sure that pointers (or references) of copied objects point to a new copy of the dynamic resource allocated manually in the copy constructor using new operators.
#include<iostream>
using namespace std;
class DeepCopyExample {
private:
int* data;
public:
// Constructor
DeepCopyExample(int value) {
data = new int(value); // Allocate memory and initialize
cout << "Constructor: Memory allocated at " << data << endl;
}
// Deep Copy Constructor
DeepCopyExample(const DeepCopyExample &obj) {
data = new int(*obj.data); // Allocate new memory and copy the value
cout << "Copy Constructor: Memory allocated at " << data << endl;
}
// Destructor
~DeepCopyExample() {
cout << "Destructor: Memory freed at " << data << endl;
delete data; // Free the allocated memory
}
// Function to display the value and its memory address
void display() const {
cout << "Value: " << *data << ", Address: " << data << endl;
}
};
int main() {
DeepCopyExample obj1(100); // Constructor is called
DeepCopyExample obj2 = obj1; // Copy constructor is called
// Display values and their addresses
cout << "obj1: ";
obj1.display();
cout << "obj2: ";
obj2.display();
return 0;
}
Constructor: Memory allocated at 0x18bd99c3870
Copy Constructor: Memory allocated at 0x18bd99c3890
obj1: Value: 100, Address: 0x18bd99c3870
obj2: Value: 100, Address: 0x18bd99c3890
Destructor: Memory freed at 0x18bd99c3890
Destructor: Memory freed at 0x18bd99c3870
obj1.obj2, showing that a separate memory allocation occurred for the deep copy.display function confirms that obj1 and obj2 have different memory addresses for their data members.obj1 and obj2 free the respective memory addresses, ensuring that there are no memory leaks.
excellent
nice
Good
You must be logged in to submit a review.