The quintessential example of this is an array of pointers to structs or objects (that are mutable).
A shallow copy copies the array and maintains references to the original objects.
A deep copy will copy (clone) the objects too so they bear no relation to the original. Implicit in this is that the object themselves are deep copied. This is where it gets hard because there's no real way to know if something was deep copied or not.
The copy constructor is used to initilize the new object with the previously created object of the same class. By default compiler wrote a shallow copy. Shallow copy works fine when dynamic memory allocation is not involved because when dynamic memory allocation is involved then both objects will points towards the same memory location in a heap, Therefore to remove this problem we wrote deep copy so both objects have their own copy of attributes in a memory.
In order to read the details with complete examples and explanations you could see the article Constructors and destructors.
The default copy constructor is shallow. You can make your own copy constructors deep or shallow, as appropriate. See C++ Notes: OOP: Copy Constructors.