View Single Post
Old 01-28-2005, 08:05 PM   #21 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,546
Valmont is on a distinguished road
Page 15.

The Assignment Operator

A we've seen an own implemented copy constructor is needed to prevent double deletion when one object is used to initialise the other object of the same type or class. We need an overloaded assignment operator as well to prevent double deletion. But there is a second reason why an overloaded assignment operator is needed: to enforce proper deletion.

Double Deletion

We will first cover double deletion.
This time we would like to prevent an attempt to double delete dynamically allocated memory if one object receives its data by assignment from another object of the same type or class. Below is a image to demonstrate once again the difference between assignment and initialisation.



As you can see, the assignment operator has two meanings here. In the first snippet, the copy constructor is invoked. In the second example the assignment operator is invoked. If you didn't provide an overloaded version then C++ will provide a default version. In the case where members allocate memory dynamically, we can't use the default assignment operator.

Time to demonstrate what is going under the hood again. You must use the String class as we defined it on the previous page. Thus with a copy constructor and an implemented destructor. Then let's find out what the function below does:
Code:
int main()
{
  String A("Hello");
  String B;
  B=A;

  return 0;
}
Again, this oh-so simple code causes us headaches if we rely on the default provided assignment operator. Look at the image below and observe what this snippet does:



As you can see, on destruction the behaviour is the same as our class without a copy constructor. Sadly enough a system with this implementation doesn't have to crash. The standard never defined a crash to start with. So we need to add a little code to demonstrate the double deletion. We need to modify the destructor slightly:
Code:
String::~String()
{
  cout<<"dtor called for: "<<this<<endl;
  cout<<"Output = "<<m_pChars<<endl;
  
  delete[] m_pChars;
}
Now lets run this program in a console (don't debug, just run the executable in the console or bash). This is what I see:



As you can see, the first call where object A is destroyed, the pointer m_pChars holds valid data, but the second call to the destructor, where object B is destroyed, the pointer does not point to the original data (memory). So deletion goes wrong. We need to prevent this. There is another issue.

Memory Leaks AND Double Deletion

Add now this to your code:
Code:
//---------------------------------------------------------- void change(String& s)
{
  String B("I am string B");
  B=s;
}

//---------------------------------------------------------- int main()
{
  String A("Hello");
  change(A);

  return 0;
}
That's right, leave our String class intact, and change() is a free function.
Run the application in console again and and observe the output. As you will see, the output is almost the same. Most likely this will resort in a different address-range. I will explain first with words what is going on, then I will present a big image to visualize the whole process in memory. This happens:


1) Object A is initialised.
2) Object A is passed to change() by reference.
3) Object s points to the same memory location as object A does.
4) Object B is initialised.
5) Object B points to the same memory location as Object A does.
6) Function exits, object B goes out of scope and calls its destructor. It deletes the same memory block as object A points to!
7) In main() object A goes out of scope as well, when the application stops. So the destructor for object A is called as well. But A's pointer points to nothing. BOOM.
8) The string "I am string B" remains in memory. It was never deleted!


A lot of things happen here. Time for an image where the 8 steps are visualised:



The image is not strictly accurate. But it shows clearly what the problems are. Time to solve them. We will explore both efficiency and correctness.
__________________
Valmont is offline   Reply With Quote