Previous Table of Contents Next


When an object is defined as local to a function, it’s destroyed as soon as the function terminates. Global objects terminate when the program ends.

The general problem is that there’s a need to take certain actions when the object is created and later when it’s destroyed. Fortunately, C++ makes object initialization and cleanup easy by providing constructors and destructors. These special member functions control how an object is created or destroyed. The naming syntax is unusual: for a given class,

  the name of a constructor is class, and
  the name of a destructor is ~class.

Our string class is named CStr, so the constructor and destructor are named CStr and ~CStr, respectively:

class CStr {
   char *pData;
   int nLength;
public:
   CStr();    // Constructor
   ~CStr();  // Destructor

char *get(void);
     int getlength(void);
     void cpy(char *s);
     void cat(char *s);
};

Constructors and destructors have some quirks, One quirk is that they have no return type of any kind—not even void. This is an apparent exception to the rule that every C++ function must have a return type. Another quirk is that the argument lists are left blank rather than using void.

The function definition for the constructor initializes the two class variables as well as allocates a one-byte string that consists of a single null terminator:

CStr::CStr() {
   pData (char *) malloc(1);
   *pData ‘\0’;
   nLength = 0;
}

Odd, isn’t it? The name “CStr” appears twice in the function heading. The first occurrence of CStr should be taken together with the scope operator (::) as an indicator that this function is a member of the CStr class. So “CStr::” is a prefix. The second occurrence of CStr is the name of the function itself.

The destructor is simpler. All it has to do is to give the current memory block back to the system by calling the free function. Remember that the name of the function is ~CStr.

CStr:: ~CStr() {
   free(pData);
}

Destructors tend to be somewhat cut and dried: all a destructor does, generally, is to free any loose system resources hanging around; then it ends. Constructors, however, have more interesting possibilities. You’ll learn more about them in the next chapter, “Another Look at Constructors.”

Function Inlining for Fun and Profit

OK, you say, encapsulation sounds wonderful, but it’s too inefficient. Remember, encapsulation is the idea that internals such as pData and nLength cannot be accessed from outside. The user of an object has to call public functions to get at the values.

Encapsulation has many benefits, but it also means that you often end up with one-line functions such as these:

char *CStr::get(void) { // Return ptr to string data.
return pData;
}

int CStr::getlength(void) { // Return length.
   return nLength;
}

There’s a certain overhead associated with each function call, so calling a function rather than just getting a value slows down performance. Fortunately, C++ provides an optimal solution: inline functions. The term inline refers to expanding something directly into the body of the code (that is, placing it inline) rather than executing a jump via the processor’s CALL instruction. For extremely short functions, the inline approach is ideal. In this case, if getlength were an inline function, then the compiler would, in effect, replace this statement:

x = string1.getlength();

with this:

x = string1.nLength;

This arrangement eliminates function-call overhead and preserves execution speed.

Normally, this second C++ statement would be illegal (nLength is private and can’t be accessed), but because nLength was accessed through a public member, getlength, there is no problem. The inline function approach preserves both encapsulation and efficiency.

In C++, functions are automatically inlined when you place them inside the class declaration. Here this is done for get and getlength:

class CStr {
   char *pData;
   int nLength;
public:
   CStr(); // Constructor
   ~CStr(); // Destructor

   char *get(void) {return pData;}
   int getlength(void) {return nLength;}
   void cpy(char *s);
   void cat(char *s);
};

Despite their brevity, get and getlength are functions and obey almost all the same syntax rules as normal functions do. Each of them could have had more than one statement if you chose, although most inline functions are very short. Note that, as with standard function definitions, the closing brace in these function definitions is not followed by a semicolon.


Previous Table of Contents Next