View Single Post
Old 01-28-2005, 08:08 PM   #22 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,546
Valmont is on a distinguished road
fp_unit:
This page isn't finished yet. Don't know when I have the time. But it gives you a starter.

Page 16:

The Assignment Operator: implementation

First the template, then the complete working code for you to try out. The skeleton for operator=:
Code:
String@1 String::operator=(const String& s) 
{
  if (this != &s)
  { 
    ...
  }
  return @2;
}
The mystery guests @1 and @2 aren't so mystery anymore because we covered them in detail earlier, where we implemented operator+= for the Complex class. Try to figure them out. Soon I will present them without an explanation.

QUESTTION: What was the purpose of the two mystery guests again?
Figure that out, and you'll know what code to type.


But something new is presented. A new rule of engagement. In case of overloading the assignment operator, we must check for self assignment.
Example 1: A = A
Example 2: A = B (if B is a reference to A!) A tricky one. This is called aliasing.

There are two good reasons to cope with possible self-assignment:
  • Efficiency.
  • Correctness (avoiding bugs again).

Efficiency

This is the lesser (and easier) part. Assignment basically means: change the state of the object. Assignment to self doesn't change the state at all, so to prevent executing more code than is needed, we have to be weary for self assignment.

Changing the state of an object usually means changing value of the properties for that object. In our case we are talking about m_pChars.

Correctness (part 1 of 2)

If assignment means that we are changing the state of an object (by changing the value of its properties in our case), then it means we need to assign a new value to the property for that object. We have to find out how this is done in the first place. So never mind correctness for now, and let us first implement a seemingly functional operator=. Once we have such an implementation, we can move back a little and find out about correctness in detail.

Moving On With The String Class: operator=

Assigning boils down to changing the value of m_pChars in our specific case. But m_pChars always contains data. The constructor String::String(const char* s = "") will make sure of that: If m_pChars does not contain user-defined data, then this variable holds an empty string. Note that the memory for this data is dynamically allocated. Therefore, assigning this variable with new data just like that, will keep the old data still present in memory. It becomes inaccessible and so that part of memory is lost for good. So we delete the old data first correctly. Remember, m_pChars is an array of C-style characters so use the proper delete keyword (in this incomplete code snippet):
Code:
String& String::operator=(const String& s) 
{
  {
    delete[] m_pChars;
  }
  return *this;
}
The mystery guests are finally filled in as well: a return by reference for chaining, and *this to return an object. The return by reference prevents the creation of temporaries and returning the object (*this) makes sure the next call is a valid call by an object. Reread overloading operator+= in the Complex class for the details if needed.

At the very beginning of this chapter we discussed how to deal with assigning, copying and setting up new string data for a C-style string array:
  • Calculate the needed size of the array..
  • Allocate the proper amount of new memory.
  • Use strcpy() when copying or assigning data.
So here are the next three lines:
Code:
String& String::operator=(const String& s) 
{
  {
    delete[] m_pChars;
    int len = strlen(s.m_pChars) + 1;
    m_pChars = new char[len];
    strcpy(m_pChars, s.m_pChars);
  }
  return *this;
}
This is a fully functional implementation. But it is buggy. I'll present the complete code (in case you lost track) for you to copy so we can quickly set up a test in main():
Code:
#include <iostream>
#include <cstring>

using namespace std;

class String
{

public:
  String& operator=(const String& );
public:
  ~String ();
  String(const char* s = "");
  String(const String& );
private:
  char *m_pChars;
};

//------------------------------------------------------- //Acts like a conversion operator: 'C-style string' to 'String'
String::String(const char *s)
{
// Need to copy the given string. if s is zero, make a null string. if (s)
  {
    int len = strlen(s) + 1;
    m_pChars = new char[len];
    strcpy(m_pChars, s);
  }
  else
  {
    m_pChars = new char[1];
    *m_pChars = '\0';
  }
}

//---------------------------------------------------------

String::~String()
{
  delete[] m_pChars;
}

//----------------------------------------------------------

String::String(const String& s)
{
  m_pChars = new char[strlen(s.m_pChars) + 1];
  strcpy(m_pChars, s.m_pChars);
}

//----------------------------------------------------------

String& String::operator=(const String& s)
{
  {
    delete[] m_pChars; // first delete old int len = strlen(s.m_pChars) + 1; // +1 for '\0'
    m_pChars = new char[len];
    strcpy(m_pChars, s.m_pChars);
  }
  return *this;
}

//----------------------------------------------------------
And here is the main() so we can do a test:
Code:
int main()
{
  String A("Hello");
  String B;
  B=A;

  return 0;
}
The code is buggy and delivers an undefined result. We should test it by debugging. Insert a breakpoint at B=A; and single step through the code. You will see that the contents of B are cleared first (we free the memory nicely), then the contents of A are copied into B. Nothing weird or surprising here. It seems to work correct.
__________________
Valmont is offline   Reply With Quote