In C++, a duplicate constructor is a special type of constructor responsible for generating a fresh object through replicating the data from an existing object within the identical class. This constructor gets invoked whenever an object is being set up using another object from the same class. This operation, referred to as copy initialization, encompasses transferring the values from each data component of the initial object to the newly created one.
In C++, a copy constructor executes a member-wise duplication process, where it duplicates each data member of an object separately to generate a fresh object. Similar to all other constructors in C++, a copy constructor does not have a return type, which includes void.
Syntax
It has the following syntax:
Class_Name (const Class_Name &object);
In this particular format,
- Class_Name refers to the title of the class, serving as the class constructor.
- const Class_Name &object:
It serves as a constant pointer to an instance of the identical class, which is provided as an argument.
Simple C++ Copy Constructor Example
Let's consider an example to illustrate the Copy Constructor.
Example
#include <iostream>
using namespace std;
class Copy_construct
{
public:
int d;
Copy_construct()
{
d = 0;
}
Copy_construct(const Copy_construct &s)
{
d = s.d;
}
};
int main() //Main
{
Copy_construct obj_1;
obj_1.d = 35;
cout << "obj_1 data is: " << obj_1.d << endl;
Copy_construct obj_2(obj_1);
cout << "obj_2 data is: " << obj_2.d << endl;
return 0;
}
Output:
obj_1 data is: 35
obj_2 data is: 35
Explanation
In this instance, we are outlining a class named Copyconstruct that consists of a constructor that is both default and for copying. The copying constructor is employed to instantiate an object obj2, which mirrors the value of d from obj_1, and then both values are displayed.
Characteristics of Copy Constructor in C++
Several characteristics of copy constructor in C++ are as follows:
- A copy constructor initializes a new object by copying data from an existing instance of the same class.
- It accepts a reference to an object of the same class as an argument.
- Copy initialization refers to the process of utilizing a copy constructor to initialize members of another object.
- The copy constructor does a member-wise copy, which means it transfers each member variable from the source object to the new object.
- If no copy constructor is explicitly defined, the compiler will build a default copy constructor.
- Programmers can create their own copy constructor to handle special copy behaviours like deep copying or resource management.
- Using a reference in the parameter avoids unnecessary copying and ensures better performance.
Types of Copy Constructor
There are primarily two categories of Copy Constructor in C++.
Next, we will examine each of these copy constructors individually.
1) Default Copy Constructor
In C++, the default copy constructor is determined by the compiler. When the user does not define a copy constructor, the compiler generates its own constructor. If a copy constructor is not explicitly declared, C++ automatically creates a default copy constructor. This built-in constructor copies the data from the original object to the new object without requiring any customized directives from the programmer.
C++ Default Copy Constructor Example
Let's consider an example to demonstrate the default copy constructor in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class Rect
{
int width, height;
public: //Access Modifier
Rect(int w, int h)
{
width = w;
height = h;
}
int getWidth()
{
return width;
}
int getHeight()
{
return height;
}
};
int main() //Main Function
{
Rect r1(15, 58);
Rect r2 = r1;
// Default copy constructor is called
cout << "Width is: " << r2.getWidth() << "\nHeight is: " << r2.getHeight();
return 0;
}
Output:
Width is: 15
Height is: 58
Explanation
In this instance, the Rect class contains a constructor that defines the width and height parameters. Upon creating r2 as a duplicate of r1, the inherent copy constructor duplicates the width and height attributes from r1 to r2, enabling retrieval of the identical values through the getWidth and getHeight functions.
Usage of Default Copy Constructor
Several scenarios where the default copy constructor in C++ is commonly utilized include:
- It proves beneficial for classes that refrain from employing pointers or dynamic memory allocation, and when duplicating member variables in a superficial manner suffices.
- In instances where a particular copy functionality is not mandated, relying on the compiler's inherent copy constructor minimizes development complexities and maintains code elegance.
2) User-Defined Copy Constructors in C++
The developer specifies the custom constructor for users. If a developer designs a copy constructor to tailor object duplication, it is referred to as a user-defined or explicit copy constructor.
A custom copy constructor enables the developer to specify the process of replicating data from one instance to another. This feature proves valuable in scenarios where the default copying mechanism falls short, especially when dealing with dynamic memory allocation or specialized operations.
The developer guarantees that the fresh instance accurately duplicates the initial one by meticulously specifying the replication procedure, granting them full authority over the initialization of members.
C++ User-Defined Copy Constructor Example
Let's consider an example to illustrate the User Defined Copy Constructor in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class Product
{
public: //Access Modifier
int price;
Product(int pro)
{
price = pro;
}
Product(const Product &item)
{
price = item.price;
}
};
int main() //Main Function
{
Product p1(500);
Product p2(p1);
cout << "Price of copied product: " << p2.price; //Displays the output
return 0;
}
Output:
Price of copied product: 500
Explanation
In this instance, we define a Product class containing a customized copy constructor that accepts parameters, facilitating the transfer of price data between objects. Within the main function, a new object p2 is instantiated to replicate p1, enabling the display of the duplicated price information.
Usage of User-Defined Copy Constructor
Several scenarios where a custom copy constructor in C++ is beneficial include:
- It is commonly employed to track or record the instances when an object is duplicated.
- In situations where shallow copying suffices but extra functionalities (like tallying and verification) need to be incorporated during the copying process.
Behavior of Copy Constructor
In C++, a copy constructor serves as a unique constructor designed for generating a fresh object identical to an already existing one. The copy constructors are responsible for two primary forms of copying mechanisms:
- Shallow Copy
- Deep Copy
1) Shallow Copy Constructor in C++
In C++, a shallow copy constructor allows a developer to define the process of replicating the values of one object into another, incorporating unique operations like logging or monitoring. Only non-pointer attributes are replicated outright in shallow copying, potentially resulting in shared underlying resources between both objects.
A shallow duplication can exclusively be generated through the default constructor. This method proves to be highly beneficial and efficient in scenarios where dynamic memory allocation is unnecessary.
C++ Shallow Copy Example
Let's consider an illustration to showcase the Shallow Copy Constructor in C++.
Example
#include <iostream>
using namespace std; //using standard namespace
class shallow_copy //class
{
public: //Access Modifier
int cod;
shallow_copy(int c) : cod(c) {}
shallow_copy (const shallow_copy & i) : cod(i.cod)
{
cout << "Copied Item with code: " << cod << endl; //prints the output
}
};
int main() //Main Function
{
shallow_copy s1(200);
shallow_copy s2 = s1; //shallow copy
return 0;
}
Output:
Copied Item with code: 200
Explanation
In this instance, we are examining a shallowcopy class that includes an integer attribute cod, set during object initialization. Upon creating object s2 through s1 (shallowcopy s2 = s1;), the copy constructor is invoked to duplicate the cod value from s1 to s2. Additionally, the constructor generates a notification to verify the duplication process.
2) Deep Copy Constructor in C++
Creating a deep copy involves dynamically allocating memory for the duplicate and then replicating the original value. Each the original and duplicate objects have separate memory addresses. This ensures that both objects are unique and do not reference the same memory location. To perform a deep copy, a custom constructor needs to be implemented. Deep copy constructors enable precise management of resource duplication, ensuring object autonomy and secure memory management.
C++ Deep Copy Constructor Example
Let's consider an example to showcase the User Defined Deep Copy Constructor in C++.
Example
#include <iostream>
#include <cstring>
using namespace std; //Using Standard Namespace
class Deep_Copy
{
char* cont;
public:
Deep_Copy(const char* input)
{
cont = new char[strlen(input) + 1];
strcpy(cont, input);
}
Deep_Copy(const Deep_Copy & source)
{
cont = new char[strlen(source.cont) + 1];
strcpy(cont, source.cont);
}
~ Deep_Copy()
{
delete[] cont;
}
void display()
{
cout << "File content: " << cont << endl;
}
};
int main() //Main Function
{
Deep_Copy d1("Backup File");
Deep_Copy d2 = d1;
d2.display();
return 0;
}
Output:
File content: Backup File
Explanation
In this instance, we're establishing a Deep_Copy class responsible for managing dynamic memory related to a string. This is achieved through the implementation of a deep copy constructor that guarantees individual memory allocation for each object. By replicating the content of the original object, the deep copy constructor mitigates concerns such as shared memory or data corruption that may arise when modifying or deleting an object.
When the Copy Constructor is invoked?
In C++, the copy constructor is often invoked in the following cases:
- When a function returns an object of a given class by value.
- When an object is supplied as a value to a function.
- When a new object is initialized by referencing an existing object of the same class.
- When the compiler creates a temporary object as part of expression evaluation.
However, because of compiler optimizations such as Return Value Optimization (RVO), there is no absolute assurance of the copy constructor being executed in all scenarios. These optimizations empower the compiler to eliminate redundant copies, leading to enhanced performance.
Copy Elision
Copy elision in C++ is an optimization technique employed by compilers to remove redundant object copying, especially when returning objects or invoking functions. This optimization enhances efficiency by cutting down on needless constructor and destructor calls, proving especially beneficial when handling sizable or resource-heavy objects.
C++ Copy Elision Example
Let's consider an example to illustrate the Copy Elision concept in C++.
Example
#include <iostream>
using namespace std; //Using Standard Namespace
class cpp tutorial
{
public:
int data;
cpp tutorial() : data(0)
{
std::cout << "Default constructor\n";
}
cpp tutorial(int d) : data(d)
{
std::cout << "Parameterized constructor\n";
}
cpp tutorial(const cpp tutorial & g) : data(g.data)
{
std::cout << "Copy constructor\n";
}
~ cpp tutorial()
{
cout << "Destructor\n";
}
};
cpp tutorial generateGadget()
{
cpp tutorial temp(50);
return temp;
}
int main()
{
cpp tutorial finalGadt = generateGadget();
return 0;
}
Output:
Parameterized constructor
Destructor
Explanation
In this instance, we are outlining a cpptutorialech class containing constructors and destructors that record their invocations. This setup enables us to observe the instantiation and destruction of objects. Within the main function, a cpp tutorial object is produced from a function through value return, potentially triggering copy elision. Subsequently, it is assigned to another object, showcasing the behavior of object lifecycle.
Differences between Copy Constructor and Assignment Operator(=) in C++
Some variances between Copy Constructor and Assignment Operator in C++ are outlined below:
| Copy constructor | Assignment operator(=) |
|---|---|
| Creates a new object as a copy of an existing object. | Assigns values from one existing object to another existing object. |
{
// body of the constructor.
} | Syntax of Assignment operator:Class_name a,b;
b = a; |
| The copy constructor is invoked when the new object is initialized with the existing object.The object is passed as an argument to the function.It returns the object. | The assignment operator is invoked when we assign the existing object to a new object. |
|---|---|
| Both the existing object and the new object share different memory locations. | Both the existing object and the new object share the same memory location. |
| If a programmer does not define the copy constructor, the compiler will automatically generate the implicit default copy constructor. | If we do not overload the "=" operator, the bitwise copy will occur. |
| When a new object is initialized from an existing object. | When an already created object is assigned another's value. |
Class_name(const class_name &object_name)
{
// body of the constructor.
}
Class_name a,b;
b = a;
C++ Copy Constructor MCQs
1) When the Copy constructor is called in C++?
- When an object is passed by reference.
- When an object is returned by value.
- When an object is assigned using =
- None of the above.
2) What kind of copy does the default copy constructor perform in C++?
- Deep copy
- Bitwise copy
- Shallow copy
- No copy
3) Which scenarios necessitate a user-defined copy constructor in C++?
- When using only primitive data types
- When the class contains static data
- When the class has no constructor
- When dynamic memory allocation is used
4) What happens if we don't include a copy constructor in a class with dynamic memory in C++?
- Memory leak or undefined behavior
- Compilation error
- Runtime error
- Nothing unusual
5) Which of the following represents the correct signature of a copy constructor in C++?
- ClassName (ClassName obj)
- ClassName (ClassName& obj)
- ClassName (const ClassName& obj)
- ClassName (ClassName * obj)