You make a copy of a friend's "Road Trip" playlist, rename it "Gym Mix" and add a few songs. Next day your friend asks why their road trip playlist suddenly has workout songs in it. Your "copy" was not really a separate playlist: the song list inside it was still shared. That is the difference between a shallow copy and a deep copy, and it causes some of the most confusing bugs in object-oriented code.

Shallow copy = a new outer object, but everything inside it is shared with the original.
Deep copy = a new outer object AND new copies of everything it owns, so the two are fully independent.

Real-world analogy: Sharing a Google Docs link with a friend is a shallow copy: you each have your own link, but both open the same document, so an edit by either of you shows up for both. File > Make a copy is a deep copy: your friend gets a separate document, and nothing they change touches yours.

1. Three Ways to "Copy" an Object

When people say "copy", they can mean three very different things:

New outer object?New inner list?New inner objects?Changes visible in both?
Reference copy (b = a)NoNoNoEverything
Shallow copyYesNoNoChanges to shared inner data
Deep copyYesYesYesNothing

The difference only matters when an object holds other mutable objects: lists, arrays, maps or other class objects. If a class holds only numbers and strings, a shallow copy and a deep copy behave the same.

2. Reference Copy: Not a Copy at All

In Java, Python and JavaScript, a variable that holds an object really holds a reference (an address) to it. Assigning it to another variable copies the reference, not the object. Both names now point to the same playlist.

Output

roadTrip: My Mix [Kesariya, Naatu Naatu, Jai Ho]
mine:     My Mix [Kesariya, Naatu Naatu, Jai Ho]
Same object? true

Renaming mine renamed roadTrip, because there is only one object. C++ is different: Playlist mine = roadTrip; calls the copy constructor and creates a real copy. To get a second name for the same object in C++, you use a reference (Playlist&) or a pointer.

3. Shallow Copy: New Box, Same Contents

A shallow copy creates a new outer object and copies each field into it. For a number or a string, that copies the value. For a list or another object, it copies only the reference, so the inner data ends up shared.

Output

original: Road Trip -> Kesariya:5, Naatu Naatu:0, Believer:0
copy:     Gym Mix -> Kesariya:5, Naatu Naatu:0, Believer:0
Same object? false
Same song list? true

Look at what happened to each kind of field:

  • name changed only in the copy. Assigning copy.name = "Gym Mix" replaces the copy's own field; the original keeps "Road Trip".
  • The new song appeared in both. copy.songs and original.songs are the same list, so adding to one adds to "both".
  • The rating changed in both. The Song objects inside the list are shared too.

Most built-in copy tools are shallow:

  • Java: Object.clone() (default), new ArrayList<>(list), List.copyOf(), Arrays.copyOf(). A copied list is a new list, but it holds the same element objects.
  • Python: copy.copy(obj), list(items), items[:], dict.copy().
  • JavaScript: spread {...obj} and [...arr], Object.assign(), Array.from(), arr.slice().
  • C++: the compiler-generated copy constructor copies each member. That is a deep copy for value members such as std::string and std::vector<Song>, but a shallow copy for pointer members, which is why the example stores its list behind a shared_ptr.

4. Deep Copy: Everything Is Independent

A deep copy creates a new outer object, a new list, and a new copy of every object inside the list. After that, nothing is shared: any change to the copy stays in the copy.

Output

original: Road Trip -> Kesariya:0, Naatu Naatu:0
copy:     Gym Mix -> Kesariya:5, Naatu Naatu:0, Believer:0
Same song list? false
Same first song? false

How each language does it:

  • Java: write a copy constructor (new Playlist(other)) that creates a new list and copies each element. This is clearer and safer than overriding clone().
  • C++: write your own copy constructor and copy assignment operator that allocate new storage (the Rule of Three: if you need a custom destructor, copy constructor or copy assignment, you almost always need all three). Better still, store members by value (std::vector<Song>) and the default copy is already deep.
  • Python: copy.deepcopy(obj) walks the whole object graph and copies everything, and it handles cycles too.
  • JavaScript: structuredClone(obj) deep-copies plain data, but the result is a plain object without your class methods. For class instances, write a deepCopy() method. (JSON.parse(JSON.stringify(obj)) also loses methods, dates and undefined values.)

5. The C++ Pointer Trap

In C++, a shallow copy is not just confusing, it can crash your program. If a class owns memory through a raw pointer and you do not write a copy constructor, the default one copies the pointer. Both objects then point to the same memory, and both destructors try to free it.

class Buffer {
    int* data;
    int size;
public:
    Buffer(int n) : data(new int[n]), size(n) {}
    ~Buffer() { delete[] data; }
    // No copy constructor: the compiler copies the pointer (shallow)
};

Buffer a(10);
Buffer b = a;   // b.data == a.data
// end of scope: b frees data, then a frees the SAME data again -> undefined behaviour

Fix it with a deep-copying copy constructor and assignment operator, or better, avoid raw owning pointers: use std::vector, std::string or std::unique_ptr, which already copy (or refuse to copy) correctly. For a class that should never be copied, such as a database connection, delete the copy operations: Connection(const Connection&) = delete;

6. Practical Example: Question Paper Sets (Prototype Pattern)

An exam department prepares "Set A" of a question paper, then needs "Set B" with a few changes. Building Set B from scratch would repeat all the work. Instead, Set B is created by copying Set A and editing the copy. Creating new objects by copying an existing one is the Prototype design pattern, and it only works if you choose the right kind of copy for each field.

Output

Set A @ Hall A, JNTU Hyderabad (total 17)
  Q1. Define a class [2]
  Q2. Explain inheritance with an example [5]
  Q3. Design a parking lot [10]
Set B @ Hall A, JNTU Hyderabad (total 17)
  Q1. Define a class [2]
  Q2. Explain polymorphism with an example [5]
  Q3. Design a parking lot [8]
  Q4. What is an immutable object? [2]
Same hall object? true
Same question list? false

Why This Design Works

  • Owned data is deep-copied. The questions belong to the paper. Editing Q2 and Q3 in Set B did not touch Set A, and Set A still totals 17 marks.
  • Shared data is shared on purpose. Both sets are written in the same hall, so copyAs() passes the same ExamHall object. Copying it would create a second "Hall A" that could drift out of sync with the real one.
  • Immutable fields need no copying. Strings such as the question text cannot change, so sharing them is safe.
  • Python note: copy.deepcopy() would copy the hall as well. A hand-written copy_as() (or a custom __deepcopy__) lets you decide field by field.

7. Which Copy Should You Use?

Shallow copyDeep copy
Speed and memoryFast, copies only the top levelSlower, copies the whole object graph
IndependenceInner objects are sharedFully independent
Good forObjects that hold only primitives, strings or immutable objects; data that is meant to be sharedObjects that own mutable lists or objects; prototypes, snapshots, undo history, test fixtures
RiskUnexpected changes through shared dataPerformance cost, and copying things that should be shared (connections, caches)

If the inner objects are immutable, you get the best of both: a shallow copy is safe because the shared parts can never change. This is one more reason to prefer immutable value objects (see the Immutability article).

8. Common Mistakes

  • Thinking b = a makes a copy in Java, Python or JavaScript. It only creates a second name for the same object.
  • Assuming a copied list copies its elements. new ArrayList<>(list), list[:] and [...arr] create a new list of the same objects.
  • Using {...obj} on nested data and then changing a nested field. Only the top level was copied.
  • Relying on Java's clone() without overriding it to copy mutable fields. The default clone() is shallow.
  • Writing a C++ class with a raw owning pointer and no copy constructor, copy assignment and destructor (Rule of Three).
  • Deep-copying shared resources such as a database connection, a logger or a cache. Some things should stay shared.

Summary

  • Reference copy (b = a): one object with two names.
  • Shallow copy: a new outer object whose inner lists and objects are shared with the original.
  • Deep copy: a new outer object plus new copies of everything it owns; the two are fully independent.
  • Most built-in copy tools (clone(), copy.copy(), spread, Object.assign()) are shallow. For deep copies use copy constructors, copy.deepcopy(), structuredClone() or your own deepCopy() method.
  • In LLD, deep-copy what an object owns, share what is meant to be shared, and make inner objects immutable whenever you can.