C++ Shallow and Deep Copy Mechanics
Shallow Copy vs Deep Copy
Shallow Copy Mechanics
Shallow copying occurs when multiple pointers reference the identical memory address. Altering the data through any of these pointers modifies the underlying memory, affecting all other pointers pointing to that location.
Procedural Shallow Copy Illustration
Consider an array of character pointers storing inputs:
#include <iostream>
int main() {
char tempBuffer[64];
const char* wordRefs[3];
for (int idx = 0; idx < 3; ++idx) {
std::cin >> tempBuffer;
wordRefs[idx] = tempBuffer; // Shallow assignment
}
for (int idx = 0; idx < 3; ++idx) {
std::cout << wordRefs[idx] << " ";
}
return 0;
}
Entering Alpha Beta Gamma produces the output Gamma Gamma Gamma.
Assume tempBuffer resides at memory address 0x100.
Alphais read into0x100, andwordRefs[0]is assigned0x100.Betaoverwrites0x100, andwordRefs[1]points to0x100.Gammaoverwrites0x100, andwordRefs[2]points to0x100. Upon iteration, all elements read from0x100, yielding the last input stringGamma.
Object-Oriented Shallow Copy Illustration
When a class manages dynamically allocated memory, default copy operations perform shallow copying.
#include <iostream>
#include <cstring>
class TextBuffer {
private:
char* data;
public:
TextBuffer(const char* input) {
data = new char[std::strlen(input) + 1];
std::strcpy(data, input);
}
void modify(const char* input) {
delete[] data;
data = new char[std::strlen(input) + 1];
std::strcpy(data, input);
}
void display() const {
std::cout << data << std::endl;
}
};
int main() {
TextBuffer instanceA{"Original text"};
instanceA.display();
TextBuffer instanceB = instanceA; // Default copy constructor
instanceB.display();
instanceB.modify("Updated text");
instanceB.display();
instanceA.display();
return 0;
}
Executing this snippet demonstrates unintended side effects. instanceB is initialized from instanceA via the default copy constructor, copying the pointer data. Both objects now reference the identical heap allocation. Calling instanceB.modify("Updated text") deallocates the shared memory and assigns instanceB.data to a new block. Because instanceA.data retains the original (now dangling) address, any subsequent access like instanceA.display() yields undefined behavior—often outputting the newly written string if the allocator reuses the same memory block, or causing a crash.
Deep Copy Mechanics
Deep copying resolves this by allocating distinct memory blocks for each pointer before copying the actual data.
Procedural Deep Copy Illustration
Refactoring the previous procedural example requires explicit memory allocation and data duplicatoin:
#include <iostream>
#include <cstring>
int main() {
char tempBuffer[64];
char* wordRefs[3];
for (int idx = 0; idx < 3; ++idx) {
std::cin >> tempBuffer;
// Allocate independent memory
wordRefs[idx] = new char[std::strlen(tempBuffer) + 1];
// Copy the content
std::strcpy(wordRefs[idx], tempBuffer);
}
for (int idx = 0; idx < 3; ++idx) {
std::cout << wordRefs[idx] << " ";
}
// Release allocated memory
for (int idx = 0; idx < 3; ++idx) {
delete[] wordRefs[idx];
}
return 0;
}
Inputs Alpha Beta Gamma now correctly output Alpha Beta Gamma.
Each string receives a dedicated heap allocation via new char[], ensuring wordRefs elements point to isolated memory regions. The std::strcpy functon transfers the string payload into these unique buffers. Finally, delete[] deallocates each individual buffer to prevent memory leaks.