3.7. Initializing Objects with
Constructors
As mentioned in Section
3.6, when an object of class GradeBook (Fig.
3.5) is created, its data member courseName is initialized to the empty string by default. What if you
want to provide a course name when you create a GradeBook object? Each class you declare can provide a constructor that can be
used to initialize an object of the class when the object is created. A
constructor is a special member function that must be defined with the same name
as the class, so that the compiler can distinguish it from the class's other
member functions. An important difference between constructors and other
functions is that constructors cannot return values, so they cannot specify a
return type (not even void). Normally, constructors are declared
public. The term "constructor" is often
abbreviated as "ctor" in the literature—we generally avoid abbreviations.
C++ requires a constructor call for each
object that is created, which helps ensure that each object is initialized
before it is used in a program. The constructor call occurs implicitly when the
object is created. If a class does not explicitly include a constructor, the
compiler provides a default
constructor—that is, a constructor with no parameters. For example, when
line 46 of Fig.
3.5 creates a GradeBook object, the
default constructor is called. The default constructor provided by the compiler
creates a GradeBook object without giving
any initial values to the object's fundamental type data members. [Note: For data members that are
objects of other classes, the default constructor implicitly calls each data
member's default constructor to ensure that the data member is initialized
properly. This is why the string data member courseName (in Fig.
3.5) was initialized to the empty string—the default
constructor for class string sets the string's value to the empty string. You'll learn more about
initializing data members that are objects of other classes in Section
10.3.]
In the example of Fig. 3.7, we specify a
course name for a GradeBook object when the object is created (line
49). In this case, the argument "CS101 Introduction to C++ Programming"
is passed to the GradeBook object's constructor (lines 17–20) and used
to initialize the courseName. Figure 3.7 defines a modified
GradeBook class containing a constructor with a string
parameter that receives the initial course name.
Fig. 3.7. Instantiating multiple objects of the
GradeBook class and using the GradeBook constructor to specify
the course name when each GradeBook object is created.
1 // Fig. 3.7: fig03_07.cpp
2 // Instantiating multiple objects of the GradeBook class and using
3 // the GradeBook constructor to specify the course name
4 // when each GradeBook object is created.
5 #include <iostream>
6 using std::cout;
7 using std::endl;
8
9 #include <string> // program uses C++ standard string class
10 using std::string;
11
12 // GradeBook class definition
13 class GradeBook
14 {
15 public:
16 // constructor initializes courseName with string supplied as argument
17 GradeBook( string name )
18 {
19 setCourseName( name ); // call set function to initialize courseName
20 } // end GradeBook constructor
21
22 // function to set the course name
23 void setCourseName( string name )
24 {
25 courseName = name; // store the course name in the object
26 } // end function setCourseName
27
28 // function to get the course name
29 string getCourseName()
30 {
31 return courseName; // return object's courseName
32 } // end function getCourseName
33
34 // display a welcome message to the GradeBook user
35 void displayMessage()
36 {
37 // call getCourseName to get the courseName
38 cout << "Welcome to the grade book for\n" << getCourseName()
39 << "!" << endl;
40 } // end function displayMessage
41 private:
42 string courseName; // course name for this GradeBook
43 }; // end class GradeBook
44
45 // function main begins program execution
46 int main()
47 {
48 // create two GradeBook objects
49 GradeBook gradeBook1( "CS101 Introduction to C++ Programming" );
50 GradeBook gradeBook2( "CS102 Data Structures in C++" );
51
52 // display initial value of courseName for each GradeBook
53 cout << "gradeBook1 created for course: " << gradeBook1.getCourseName()
54 << "\ngradeBook2 created for course: " << gradeBook2.getCourseName()
55 << endl;
56 return 0; // indicate successful termination
57 } // end main
|
gradeBook1 created for course: CS101 Introduction to C++ Programming
gradeBook2 created for course: CS102 Data Structures in C++
|
Defining a Constructor
Lines 17–20 of Fig. 3.7 define a constructor for
class GradeBook. Notice that the constructor has the same name as its
class, GradeBook. A constructor specifies in
its parameter list the data it requires to perform its task. When you create a
new object, you place this data in the parentheses that follow the object name
(as we did in lines 49–50). Line 17 indicates that class GradeBook's
constructor has a string parameter called name. Note that line 17 does not specify a return type,
because constructors cannot return values (or even void).
Line 19 in the constructor's body
passes the constructor's parameter name to member function
setCourseName, which assigns a value to data member
courseName. The setCourseName member function (lines 23–26)
simply assigns its parameter name to the data member
courseName, so you might be wondering why we
bother making the call to setCourseName in line
19—the constructor certainly could perform the assignment courseName =
name. In Section
3.10, we modify setCourseName to perform
validation (ensuring that, in this case, the courseName is 25 or fewer characters in length). At that point
the benefits of calling setCourseName
from the constructor will become clear. Note that both the constructor (line 17)
and the setCourseName function (line 23)
use a parameter called name. You can use the
same parameter names in different functions because the parameters are local to
each function; they do not interfere with one another.
Testing Class GradeBook
Lines 46–57 of Fig. 3.7 define the main
function that tests class GradeBook and demonstrates initializing
GradeBook objects using a constructor. Line 49 in function
main creates and initializes a GradeBook object called
gradeBook1. When this line executes, the GradeBook constructor
(lines 17–20) is called (implicitly by C++) with the argument "CS101
Introduction to C++ Programming" to initialize gradeBook1's course
name. Line 50 repeats this process for the GradeBook object called
gradeBook2, this time passing the argument "CS102 Data Structures
in C++" to initialize gradeBook2's course name. Lines 53–54 use
each object's getCourseName member function to
obtain the course names and show that they were indeed initialized when the
objects were created. The output confirms that each GradeBook object
maintains its own copy of data member courseName.
Two Ways to Provide a Default
Constructor for a Class
Any constructor that takes no arguments
is called a default constructor. A class gets a default constructor in one of
two ways:
-
The compiler implicitly creates a
default constructor in a class that does not define a constructor. Such a
default constructor does not initialize the class's data members, but does call
the default constructor for each data member that is an object of another class.
[Note: An
uninitialized variable typically contains a "garbage" value (e.g., an
uninitialized int variable might contain -858993460, which is likely to be an incorrect value for that variable in
most programs).]
-
You explicitly define a constructor that
takes no arguments. Such a default constructor will perform the initialization
specified by you and will call the default constructor for each data member that
is an object of another class.
If you define a constructor with
arguments, C++ will not implicitly create a default constructor for that class.
Note that for each version of class GradeBook in Fig.
3.1, Fig.
3.3 and Fig.
3.5 the compiler implicitly defined a default constructor.
Error-Prevention Tip 3.2
|
Unless no
initialization of your class's data members is necessary (almost never), provide
a constructor to ensure that your class's data members are initialized with
meaningful values when each new object of your class is
created. |
Software Engineering Observation 3.4
|
Data
members can be initialized in a constructor of the class, or their values may
be set later after the
object is created. However, it is a good software engineering practice to ensure
that an object is fully initialized before the client code invokes the object's
member functions. In general, you should not rely on the client code to ensure
that an object gets initialized
properly. |
Adding the Constructor to Class
GradeBook's UML Class Diagram
The UML class diagram of Fig. 3.8 models class
GradeBook of Fig. 3.7, which has a constructor
with a name parameter of type string (represented by type
String in the UML). Like operations, the
UML models constructors in the third compartment of a class in a class diagram.
To distinguish a constructor from a class's operations, the UML places the word "constructor" between guillemets (« and ») before the
constructor's name. It is customary to list the class's constructor before other
operations in the third compartment.
