home account info subscribe login search My ITKnowledge FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
To access the contents, click the chapter and section titles.

C++ in Plain English
(Publisher: IDG Books Worldwide, Inc.)
Author(s): Brian Overland
ISBN: 1558284729
Publication Date: 04/01/96

Bookmark It

Search this book:
 
Previous Table of Contents Next


Chapter 6
Another Look at Constructors

The name is perfectly apt: a constructor is a function that manufactures an object. Constructors are as vital to you as factories are to a captain of industry. You may think of constructors simply as initialization functions, but they have important subtleties.

To write your classes correctly, you often need special constructors. One of the most important functions is the copy constructor, which tells the class how to pass the object as an argument or return value. As you’ll see in the next chapter, code that extends the CStr class is likely to cause errors without a correctly written copy constructor.

This chapter is shorter than Chapter 5. There are only a few key facts you have to keep in mind about constructors, but these facts are vital to your C++ health!

Overloading: Constructors and More Constructors

When you define a string object, you may want to initialize it in a variety of ways. For example, wouldn’t it be nice to be able to place any and all of the following in your programs?

CStr a, b, c;                      // Define with no initial
                                   //value.
CStr name(“Joe Bloe”);             // Initialize from char*.
CStr name2(name);                  // Initialize from another
                                   //CStr.

Or even to combine them on the same line:

CStr title(“The Big Show”); 
CStr a, name(“Joe”), b(title);

Each of these declarations invokes a different constructor. C++ lets you write any number of constructors for the same class, in which each constructor takes a different type of argument or arguments. This is an example of the technique of function overloading, which means that the same function name can be reused in different contexts.

Remember that the name of the constructor is always the same as the name of the class itself. Here are the prototypes for the three constructors we need:

CStr();               // No initializer.
CStr(char *s);        // Initialize from char*.
CStr(CStr &str);     // Initialize from another CStr.

Let’s look at the first two constructors first. The third introduces a new operator—the reference operator (&)—and requires special commentary.

The term overloading occurs a good deal in C++ literature. In general, almost any C++ function—not just class functions—can be overloaded. Overloading means that you can reuse the same name and rely on differences in the argument list to distinguish them. Either the number of arguments or the types of arguments (or both) must differ. (Argument names are irrelevant in this case.) For example, C++ considers the following two functions to be different, and each must have its own definition:
Display(int);
Display(char*);
In case it’s not obvious, C++ calls a different function—Display(Int) or Display(char*)—depending on which type of argument it finds in source code at compile time.

A Tale of Two Constructors

The first constructor is the one originally shown in Chapter 5. This constructor has the task of initializing an empty string:

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

The CStr(char*) constructor, which initializes from a standard C string, is roughly the same length. The basic requirements for CStr constructors are always the same: allocate memory to hold string data, initialize the data, and initialize the nLength data member.

CStr::CStr(char *s) {
    pData = (char *) malloc(strlen(s));
    strcpy(pData, s);
    nLength = strlen(s);
}

In both cases, the scope operator (CStr::) is used to prefix the constructor name, because these are function definitions and occur outside the CStr declaration.

These constructors are invoked in the following example definitions:

CStr string1;                // Invoke CStr()
CStr string2(“Hello, C++.”); // Invoke CStr(char*)

Interlude with the Default Constructor

Before we move on to the third constructor, CStr(CStr&), let’s look at a special kind of constructor, the default constructor. Surprise! This is actually the first constructor we looked at: the constructor with no arguments.

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

Every class has, or should have, a default constructor. In every class, the default constructor is simply the constructor with no arguments. C++ has an interesting quirk in regard to its handling of constructors:

  If a class declares no constructors of any kind, the compiler supplies a default constructor for free. This is a hidden constructor. This function is not sophisticated; it simply initializes all data members to zero. In the case of CStr, this behavior would not be adequate, but it might be sufficient for simpler classes.
  But if you go back and add any constructors to the class declaration, the compiler-supplied default constructor goes away. It simply vanishes!

The moral of the story should be clear: you should always include a default constructor, even if that constructor doesn’t do anything. This is a form of defensive programming. It ensures that if you go back later and add other constructors, the default constructor won’t vanish. (It will vanish if you rely on the compiler-supplied default constructor, but it won’t vanish if you write your own, no matter how simple.)

Such a default constructor might well do nothing. For example:

class CHorse {
    BREED horse_breed;
    char *name;
public:
    CHorse();
};

CHorse::CHorse() {
}

The importance of having a default constructor cannot be overemphasized. It so happens that the default constructor is invoked in a number of situations: defining variables with no initial values (already mentioned) as well as declaring an array with uninitialized members.

CHorse posse[30];

Default constructors are also invoked when you use the new operator—this operator (introduced in the previous chapter) is important in C++ and occurs frequently. If the default constructor is missing, all these situations result in an error.

Given the way I’ve described the behavior of C++ (making the hidden default constructor vanish as soon as you add your own constructors), it may sound as though the compiler is trying to play a nasty trick on you. Yet the behavior makes sense if you think about it. C++ is designed to be as compatible as possible with C. This includes porting structures from C and considering them classes.
C has no notion of member functions or constructors, but simplistic classes (namely, C struct types) must be ported to C++ and work correctly. This is why the compiler supplies a hidden default constructor: to handle backward-compatibility cases where there are no member functions at all. Once you start defining new classes and writing member functions, however, you should supply your own default constructor.


Previous Table of Contents Next
[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.