We copy the contents of one instance to another many a times, with out knowing undefined behaviour which it may some times lead to. Let's work on with an example -
class foo { int* ptr; int var; public: foo() { var = 10; ptr = new int; *ptr = var; } ~foo() { delete ptr; } };
The good things about this very simple foo interface is -
- It's using constructor to initialize it's member variables.
- It's giving back the resources to the free store in the destructor acquired through operator new in the constructor. It's always a good programming practice , infact it is a must, to return the resources if the programmer is managing resources. Now, though this interface seems to be very simple can lead to very complicated problems. Lets see what happens with the following code snippet -
foo objOne; foo objTwo = objOne;
Now the intention of second statement is to copy the contents of objOne to objTwo, so that each have their own set of variables. So, the construction of objTwo happens with the default copy constructor, provided by the compiler, is called to do a member-wise copy. Both the default copy constructor, copy assignment does the same operation but which one is called depends on the statement which we shall discuss in another post. The problem is with the pointer variable because the values pointed by the pointer aren't going to be copied. Instead, the content of the pointer is going to be copied. And this means both pointer variables of two instances are pointing to the same location which is definitely not what our intention is with the second statement.The potential problems that may be faced with default copy construction in this interface is - " What happens if objOne deallocates the resources managed by it ?". We have a dangling pointer variable in objTwo. So, this is where the Rule of Three comes in to the picture.
What is Rule of Three ?
If you feel the interface needs either a -
- Copy Constructor
- Copy Assignment Operator
- Destructor
then, the interface may require all the three. Now, with this rule in mind, lets modify the interface foo.
class foo { int* ptr; int var; public: foo() // Default Constructor { var = 10; ptr = new int; *ptr = var; } foo( const foo& obj ) // Copy Constructor { ptr = new int; ptr = obj.ptr; var = obj.var; } foo& operator= ( const foo& obj ) // Copy Assignment { if( this != &obj ) { delete ptr; ptr = new int; ptr = obj.ptr; var = obj.var; } return (*this); } ~foo() // Destructor { delete ptr; } };
On implementing Rule of Three to the interface foo, both objOne and objTwo will have their member variables owning their own memory locations. So, the deallocation of resources by one instance isn't going to affect the other. Thus, avoiding the famous "Undefined Behaviour" of the programming paradigm. But as long as the interface is away from raw pointers, Rule of Three is likely not a concern.
PS: Some of the content mentioned here might be wrong. I welcome suggestions to improve the post. Thanks.
No comments:
Post a Comment