Object Cloning (Shallow vs Deep Copying)

Lesson 0015 — 35 min read

Object Cloning is the process of creating an exact copy of an object in a separate memory location. Understanding the difference between Shallow Copying (sharing nested references) and Deep Copying (recursively duplicating object graphs) is vital for state preservation, preventing side-effects, and memory safety.

Why Use Object Cloning?

1. Shallow Copying

Shallow copying creates a new top-level object and copies all primitive fields. However, for reference fields (pointers or object handles), it copies only the memory address — meaning both original and cloned objects share references to the exact same nested objects.

class Address {
    String city;
    Address(String city) { this.city = city; }
}

class Person implements Cloneable {
    String name;
    Address address; // Reference field

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // Shallow copy: address reference is shared
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Address addr = new Address("Mumbai");
        Person p1 = new Person("Rahul", addr);
        Person p2 = (Person) p1.clone(); // Shallow copy

        p2.address.city = "New Delhi"; // Modifies shared Address

        System.out.println(p1.name + " lives in " + p1.address.city); // New Delhi
        System.out.println(p2.name + " lives in " + p2.address.city); // New Delhi
    }
}
#include <iostream>
#include <string>
using namespace std;

class Address {
public:
    string city;
    Address(string c) : city(c) {}
};

class Person {
public:
    string name;
    Address* address; // Pointer field

    Person(string n, Address* a) : name(n), address(a) {}

    // Copy Constructor (Shallow Copy: copies the pointer address)
    Person(const Person& other) {
        this->name = other.name;
        this->address = other.address; // Shared pointer reference
    }
};

int main() {
    Address* addr = new Address("Mumbai");
    Person* p1 = new Person("Rahul", addr);
    Person* p2 = new Person(*p1); // Shallow clone

    p2->address->city = "New Delhi"; // Modifies shared Address

    cout << p1->name << " lives in " << p1->address->city << "\n"; // New Delhi
    cout << p2->name << " lives in " << p2->address->city << "\n"; // New Delhi

    delete p1;
    delete p2;
    delete addr;
    return 0;
}
import copy

class Address:
    def __init__(self, city):
        self.city = city

class Person:
    def __init__(self, name, address):
        self.name = name
        self.address = address

def main():
    addr = Address("Mumbai")
    p1 = Person("Rahul", addr)
    p2 = copy.copy(p1) # Shallow copy via copy module

    p2.address.city = "New Delhi" # Modifies shared Address

    print(p1.name + " lives in " + p1.address.city) # New Delhi
    print(p2.name + " lives in " + p2.address.city) # New Delhi

if __name__ == "__main__":
    main()

2. Deep Copying

Deep copying creates a completely independent clone. It copies the top-level object and recursively duplicates all nested objects into fresh memory locations. Changes to the clone's nested fields have zero impact on the original object.

class Address implements Cloneable {
    String city;
    Address(String city) { this.city = city; }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return new Address(this.city);
    }
}

class Person implements Cloneable {
    String name;
    Address address;

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone(); // Shallow copy outer
        cloned.address = (Address) this.address.clone(); // Deep copy nested
        return cloned;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Address addr = new Address("Mumbai");
        Person p1 = new Person("Rahul", addr);
        Person p2 = (Person) p1.clone(); // Deep copy

        p2.address.city = "New Delhi";

        System.out.println(p1.name + " lives in " + p1.address.city); // Mumbai
        System.out.println(p2.name + " lives in " + p2.address.city); // New Delhi
    }
}
#include <iostream>
#include <string>
using namespace std;

class Address {
public:
    string city;
    Address(string c) : city(c) {}
    Address(const Address& other) : city(other.city) {}
};

class Person {
public:
    string name;
    Address* address;

    Person(string n, Address* a) : name(n), address(a) {}

    // Copy Constructor (Deep Copy: allocates new Address on heap)
    Person(const Person& other) {
        this->name = other.name;
        this->address = new Address(*other.address); // Deep copy
    }

    ~Person() {
        delete address; // Safe memory cleanup for owned object
    }
};

int main() {
    Address* addr = new Address("Mumbai");
    Person* p1 = new Person("Rahul", addr);
    Person* p2 = new Person(*p1); // Deep copy

    p2->address->city = "New Delhi";

    cout << p1->name << " lives in " << p1->address->city << "\n"; // Mumbai
    cout << p2->name << " lives in " << p2->address->city << "\n"; // New Delhi

    delete p1;
    delete p2;
    delete addr;
    return 0;
}
import copy

class Address:
    def __init__(self, city):
        self.city = city

class Person:
    def __init__(self, name, address):
        self.name = name
        self.address = address

def main():
    addr = Address("Mumbai")
    p1 = Person("Rahul", addr)
    p2 = copy.deepcopy(p1) # Deep copy via copy module

    p2.address.city = "New Delhi"

    print(p1.name + " lives in " + p1.address.city) # Mumbai
    print(p2.name + " lives in " + p2.address.city) # New Delhi

if __name__ == "__main__":
    main()

3. Customizing Copy Behavior

Languages provide specific hooks for fine-tuning cloning logic:

Comparison Summary

Aspect Shallow Copying Deep Copying
Outer Object New outer object created New outer object created
Nested Objects Shared by reference (same memory address) Recursively cloned into new memory locations
Independence Mutating nested data affects original object Completely independent memory graph
Java Mechanism Default super.clone() Manual clone() on reference fields
C++ Mechanism Default compiler copy constructor (pointer copy) Custom copy constructor with new allocation
Python Mechanism copy.copy(obj) / __copy__ copy.deepcopy(obj) / __deepcopy__

What happens in Java if a class calls super.clone() without implementing Cloneable?

Why does Python's __deepcopy__(self, memo) method accept a memo parameter?

Notes

Object Cloning :-

Cloning ⇒ Creating duplicate object in separate memory location

Use Cases ⇒ State snapshots ; undo/redo ; prototype pattern ; avoiding unwanted reference sharing

Shallow Copying :-

Behavior ⇒ New outer object ; reference/pointer fields shared with original

Side-effect ⇒ Modifying nested mutable object via clone mutates original as well

Tools ⇒ Java super.clone() ; C++ default copy ctor ; Python copy.copy()

Deep Copying :-

Behavior ⇒ New outer object + recursively cloned nested objects

Independence ⇒ Completely isolated memory graph ; no shared reference side-effects

Tools ⇒ Java manual nested.clone() ; C++ new Address(*other.addr) ; Python copy.deepcopy()

Primary source: Oracle Java Tutorials — Cloneable & Object.clone(), cppreference — Copy Constructors & Python Docs — copy module. Ask me anything that's unclear.

← Prev: Class Relationships Next: Exception Handling →

Questions? Ask your agent — you can follow up on any concept, quiz answer, or how this applies to a specific coding problem. Review the Java glossary or syntax quick reference for a compressed overview.