Shallow copy? Deep copy? Will I drown?Filed Under: Jedi Wing 2, Weekly Tuesday Dose of goodness
- Shallow copy? Deep copy? Will I drown?
- Shallow copy? Deep copy? Will I drown? - Part 2
- Shallow/Deep copy by design
Hi all,
Sorry for not posting last week as I was so swamped with babysitting and other commitments.
As promised, this week I’ll talk about something more interesting. As you might have heard about things such as a shallow and deep copy. Some people drowned when they mishandled copy operations, while some mistakenly think that a copy construction as a copy operation.
Anyway, let’s begin…
Introduction
I should probably have preceded this article with another one known as the C++ Big Three. Reason being that the big three actually has direct relation with shallow and deep copy.
Let’s get back to the basics.
Constructor, Copy Constructor, Assignment Operator and Destructor.
A constructor simply allocates the memory required for the object and calls its constructor as appropriate.
A copy constructor handles the appropriate copy operation.
An assignment operator handles the change of state when the target object is given.
A destructor obviously releases the memory held by the object.
So where does shallow and deep copy come in? Well, it comes in 2 places. The copy constructor and the assignment operator.
The question of shallow or deep depends on whether these 2 are being overriden and when overriden, whether did they perform the appropriate checks to ensure that both objects are complete unique copies and isn’t sticky in any case.
Scenarios
For today, we’ll only focus on the Copy Constructor. But we’ll still list down the few scenarios that triggers either shallow or deep copy either via copy constructor or assignment operator . Using a simple Object, here’s the declaration:
class Object
{
private:
int* ptr;
int a;
int b;
public:
Object() : ptr(new int(100)), a(0), b(0) { };
~Object() { delete(ptr); ptr=0; };
}
1) No copy constructor, no assignment operator (as above)
{
Object a;
Object b = a; //ptr is now shared between object A and B
}
// A cleans up, deleting ptr
// B cleans up, program crashes!
// Conclusion : You'll drown!
{
Object a; Object b;
a = b; //ptr of A is leaked because the default assignment operator simply assigns the address over from B }
// A cleans up, deleting ptr // B cleans up, program crashes! // Conclusion : You pee in your pants and drown!
2) Has copy constructor, no assignment operator
Let’s add-on the copy constructor, so now it looks like this:
class Object
{
private:
int* ptr;
int a;
int b;
public:
Object() : ptr(new int(100)), a(0), b(0) { };
Object(const Object& copy) : ptr(new int(*copy.ptr)), a(copy.a), b(copy.b) {};
~Object() { delete(ptr); ptr=0; };
}
Take note of this initialization : ptr(new int(*copy.ptr)) - it creates a new integer in the heap and copies the CONTENT of copy.ptr by dereferencing it onto the new integer.
3) No copy constructor, has assignment operator
- To be continued in my next article
4) Has both but does its job carelessly
- To be continued in my next article
5) Has both but does checks to ensure that 2 objects will be uniquely different
- To be continued in my next article
What’s the problem with shallow copy?
Shallow copy inherently by itself has no flaws. It is when pointer ownership kicks in and that this ownership isn’t well managed. This therefore leads to memory errors, leaks and so on.
Shallow copy can be considered as the carbon-copy of whatever that is in the Object class. However, we must also remember that a pointer is only 4 bytes. Shallow copy only copies whatever it sees, that is the Object buffer onto its new object. In this case, since the actual integer heap object is stored somewhere else, it’ll not be copied directly without an explicit dereference.
In most cases, a blantant copy of a pointer without checking the ownership will usually if not almost certainly lead to double/multiple deletion. Therefore such operations, ironically, should be reserved for those who really know what they’re doing.
In any case, following the C++ standard, a copy constructor should COPY and the assignment operator should ASSIGN as a proper copy.
When proper codes are written to handle these 2 operations, the object naturally DEEP copies itself.
What are the things that needs to be deep copied?
Basically, you don’t have to “deep” copy native types and objects. Native types are straightforward and doesn’t have an implicit representation of another object therefore it doesn’t really any deep copy.
For object wise, each of these objects should already have their respective copy constructors and assignment operators that will perform the job correctly.
The only thing you have to worry are your own pointers in your own object.
Pointers as mentioned, by itself, is only 4 bytes. The contents of the pointer is a memory address in the heap. The pointer type will then decide how much memory it’ll shadow over based on the alignments of the object type.
For a simple int*, it’ll simply take up 4 bytes in the heap.
The problem comes when there’s a need to maintain 2 seperate copies of your integer pointer. Therefore, during a copy construction, you’ll have to copy the contents of the given copy onto your own new integer object created during construction manually.
Otherwise, a shallow copy will simply copy the ADDRESS, not the POINTER. This will naturally lead you to drown.
What’re the problems associated with assignment operators?
I’d love to talk about it but it has been a long long article. Therefore, I shall sign off here and continue next week on assignment operators.
Conclusion
Thanks for looking at my articles. I certainly hope that it’ll help you in one ways or another. As promised, I’ll post the continuation of this article next week. Meanwhile, please be careful when you construct your own copy constructors and assignment operators.
Signing off,
Jeremy
- Permalink
- Admin
- 2 Feb 2010 12:28 PM
- Comments (0)