this, inline, Exceptions, Operator Overloading and other Assorted Tidbits

The material contained on this page will not be on the final exam.

During the last lecture, I discussed some C++ topics that were not present in the textbook. Some students asked me to post the notes that I presented during that lecture. Well, here they are. They are not meant to be complete, since they are notes, meaning that I used them to illustrate each topic. However, they can be used to refresh your memory of Wednesday's class.

Throughout the term, several students have asked questions relating to these topics. Some of these topics require an entire lecture to fully understand. However, I'm providing you with a peek at some of the more common features of C++ that some of you may want to pursue after this course is over. None of these topics are discussed in the textbook, so you will have to consult other books for additional information.

Also, keep in mind that the coverage here is only cursory; Some of these topics require an entire chapter of a book to adequately explain the details. Consider this page a very brief introduction to these other C++ concepts. The truth is out there...

The this Pointer

Given the specification and definition of an Integer class, what is wrong with the implementation of setValue?

class Integer
{
  public:
    Integer(void);
    void setValue(int value);
    int getValue(void);
    void Display(void) const;

  private:
    int value;
};

Integer::setValue(int value)
{
  value = value;
}

int Integer::getValue(void)
{
  return value;
}

void Integer::Display(void) const
{
  cout << value << endl;
}
The name of the parameter (value) conflicts with the private member value. We can solve this problem by explicitly qualifying the private data member:
Integer::setValue(int value)
{
  this.value = value;
}
All member functions have a "hidden" parameter named this. It is a pointer to the object that invoked the method. For example, to the compiler, this is how the function is actually seen:
Integer::setValue(Integer* const this, int value);
There are times when you need to know which object has invoked a member function and the this pointer is a mechanism that allows you to access the object. In the simple example above, the this pointer is merely used to resolve ambiguity between a private data member and a function parameter. You'll find a need to access the this pointer when implementing operator overloading.

Inline Functions

Inline functions allow programmers to protect their private data and at the same time provide efficiency for clients of the code. We can modify the Integer class to include an implicit inline function:

class Integer
{
  public:
    Integer(void);
    void setValue(int value);
    int getValue(void) { return value };
    void Display(void) const;

  private:
    int value;
};
Or, we can modify the implementation to include an explicit inline function:
class Integer
{
  public:
    Integer(void);
    void setValue(int value);
    int getValue(void);
    void Display(void) const;

  private:
    int value;
};

inline int Integer::getValue(void)
{
  return value;
}
Note that this is only a hint to the compiler and that there are other restrictions and limitations when using inline functions. Also, not all functions may be inlined. Can you think of one that might not? (a recursive function)

Exception Handling

The topic of exception handling is large and can seem complex at first. The simple examples below can prove to be quite useful. In fact, in order for my driver program for Lab #4 to run from start to finish without a core dump, it was necessary to use the techniques below.

void main(void)
{
  int a = 5, b = 0, c;
  c = a / b;            // crash and burn   
}

void main(void)
{
  int a = 5, b = 0, c;

  try
  {
    c = a / b;         
  }
  catch(...)
  {
    cout << "Caught an exception" << endl;
  }

}

-------------------------------------------------------------

void boom(void)
{
  char *p = NULL;
  cout << *p << endl;   // Kaboom!
}
 
void main(void)
{
  boom();
}

void main(void)
{
  try
  {
    boom();
  }
  catch(...)
  {
    cout << "It went BOOM!" << endl;
  }
}
This code is from the driver program used to test the StrList class implemented in Lab #4:
try 
{
  cout << "getCount() returned  " << list.getCount() << endl;
}
catch(...)
{
  cout << "ABNORMAL TERMINATION getCount()" << endl;
}
    

try
{
  cout << "isEmpty() returned " << list.isEmpty() << endl;
}
catch(...)
{
  cout << "ABNORMAL TERMINATION isEmpty() on empty list" << endl;
}


try
{
  cout << "isPresent(Ten) returned " << list.isPresent("Ten") 
       << endl;  
}
catch(...)
{
  cout << "ABNORMAL TERMINATION isPresent() on empty list" 
       << endl;
}


try
{
  list.Display();
}
catch(...)
{
  cout << "ABNORMAL TERMINATION to display empty list" << endl;
}
Overloaded Operators

The following program uses the String class we developed during the course. The program may crash because the compiler performs a shallow copy between str1 and str2. But it's not the copy that causes the problem. When the destructor is called for str1, the data pointed to by the private data member string will be deleted. When the destructor for str2 is called, the system tries to delete the data pointed to by the private data member string , but that data has already been deleted because str1 and str2 both point to the same allocated memory. Depending on the compiler you use, your program may crash or it may not. In either case, it's a Bad Thing.

void main(void)
{
  String str1("Hello");
  String str2;
  str2 = str1;
}
The fix to this is to make the assignment operator "smarter" so that it performs a deep copy instead. We do this by overloading the assignment operator. While we're at it, we will overload the '+' operator to concatenate two Strings. (This is not the entire specification of our String class.)
class String
{
  public:
    String(void);
    String(char* str);
    ~String(void);
    String(const String& otherString);
    void Set(char* str);
    void Set(const String& otherString);

      // Overloaded operators for = and +
    String operator=(String& otherString);
    String operator+(String& otherString);

  private:
    char* string;
};

  // This makes the assignment operator perform
  // a deep copy
String String::operator=(String& otherString)
{
  delete [] string;

  string = new char[strlen(otherString.string) + 1];
  assert(string);
  strcpy(string, otherString.string);
  return *this;
}

  // This allows us to concatenate two Strings
String String::operator+(String& otherString)
{
    // The length of the new string will be 
    // length of string + length of otherString.string
  int len = strlen(string) + strlen(otherString.string);
  char* tempStr = new char[len + 1];
  assert(tempStr);

    // Concatenate the strings together
  strcpy(tempStr, string);
  strcat(tempStr, otherString.string);

    // Create a new String 
  String tempString(tempStr);

    // Delete temporary array
  delete [] tempStr;

    // Return the new String
  return tempString;
}
We can test out our new operators with this code:
void main (void) 
{
  String str1("Hello");
  String str2(", ");
  String str3("World");

  String str4, str5;

    // Calls operator= to perform a deep copy
  str4 = str5 = str1;
  cout << "str4 is ";
  str4.Display(TRUE);
  cout << "str5 is ";
  str5.Display(TRUE);

    // Calls operator+ twice, then operator=
  str5 = str1 + str2 + str3;
  cout << "Now str5 is ";
  str5.Display(TRUE);
}
The output should look like this:
str4 is Hello
str5 is Hello
Now str5 is Hello, World

Back to Outline