C++ How to Program, 9/e
This chapter takes a deeper look at classes.
Coverage includes:
◦ The example also demonstrates using an include guard in headers to prevent header
code from being included in the same source code file more than once.
◦ We demonstrate how client code can access a class’s public members via the name
of an object, a reference to an object or a pointer to an object.
◦ We discuss access functions that can read or write an object’s data members.
◦ We also demonstrate utility functions—private member functions that support the
operation of the class’s public member functions.
Coverage includes (cont.):
◦ How default arguments can be used in constructors.
◦ Destructors that perform “termination housekeeping” on objects before they’re
destroyed.
◦ The order in which constructors and destructors are called.
◦ We show that returning a reference or pointer to private data breaks the
encapsulation of a class, allowing client code to directly access an object’s data.
◦ We use default memberwise assignment to assign an object of a class to another object
of the same class.
Coverage includes (cont.):
◦ const objects and const member functions to prevent modifications of objects and
enforce the principle of least privilege.
◦ Composition—a form of reuse in which a class can have objects of other classes as
members.
◦ Friendship to specify that a nonmember function can also access a class’s non-public
members—a technique that’s often used in operator overloading for performance reasons.
◦ this pointer, which is an implicit argument in all calls to a class’s non-static member
functions, allowing them to access the correct object’s data members and non-static
member functions.
Our first example (Fig. 9.1) creates class Time and tests the
class.
In Fig. 9.1, the class definition is enclosed in the following include guard:
// prevent multiple inclusions of header file
#ifndef TIME_H
#define TIME_H
...
#endif
◦ Prevents the code between #ifndef and #endif from being included if the name TIME_H has
been defined.
◦ If the header has not been included previously in a file, the name TIME_H is defined by the
#define directive and the header file statements are included.
◦ If the header has been included previously, TIME_H is defined already and the header file is
not included again.
Time Class Member Functions
In Fig. 9.2, the Time constructor (lines 11–14) initializes the data members to 0—the universal-
time equivalent of 12 AM.
Invalid values cannot be stored in the data members of a Time object, because the constructor is
called when the Time object is created, and all subsequent attempts by a client to modify the
data members are scrutinized by function setTime (discussed shortly).
You can define overloaded constructors for a class.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Before C++11, only static const int data members (which you saw
in Chapter 7) could be initialized where they were declared in the class
body.
For this reason, data members typically should be initialized by the class’s
constructor as there is no default initialization for fundamental-type data
members.
As of C++11, you can now use an in-class initializer to initialize any data
member where it’s declared in the class definition.
Parameterized stream manipulator setfill specifies the fill character that is
displayed when an integer is output in a field wider than the number of digits in
the value.
The fill characters appear to the left of the digits in the number, because the
number is right aligned by default—for left aligned values, the fill characters
would appear to the right.
If the number being output fills the specified field, the fill character will not be
displayed.
Once the fill character is specified with setfill, it applies for all subsequent
values that are displayed in fields wider than the value being displayed.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Defining Member Functions Outside the Class Definition; Class Scope
Even though a member function declared in a class definition may be
defined outside that class definition, that member function is still within
that class’s scope.
If a member function is defined in the class’s body, the compiler attempts to
inline calls to the member function.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Using Class Time
Once class Time has been defined, it can be used as a type in object, array,
pointer and reference declarations as follows:
Time sunset; // object of type Time
array< Time, 5 > arrayOfTimes; // array of 5 Time objects
Time &dinnerTime = sunset; // reference to a Time object
Time *timePtr = &dinnerTime; // pointer to a Time object
Figure 9.3 uses class Time.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Object Size
People new to object-oriented programming often suppose that objects
must be quite large because they contain data members and member
functions.
Logically, this is true—you may think of objects as containing data and
functions (and our discussion has certainly encouraged this view);
physically, however, this is not true.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
A class’s data members and member functions belong to that class’s scope.
Nonmember functions are defined at global namespace scope, by default.
Within a class’s scope, class members are immediately accessible by all of
that class’s member functions and can be referenced by name.
Outside a class’s scope, public class members are referenced through
one of the handles on an object—an object name, a reference to an object or
a pointer to an object.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Class Scope and Block Scope
If a member function defines a variable with the same name as a variable with
class scope, the class-scope variable is hidden in the function by the block-scope
variable.
◦ Such a hidden variable can be accessed by preceding the variable name with the class name
followed by the scope resolution operator (::).
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Dot (.) and Arrow (->) Member Selection Operators
The dot member selection operator (.) is preceded by an object’s name or with a
reference to an object to access the object’s members.
The arrow member selection operator (->) is preceded by a pointer to an object to
access the object’s members.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Accessing public Class Members Through Objects, References and Pointers
Consider an Account class that has a public setBalance member function.
Given the following declarations:
Account account; // an Account object
// accountRef refers to an Account object
Account &accountRef = account;
// accountPtr points to an Account object
Account *accountPtr = &account;
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
You can invoke member function setBalance using the dot (.) and arrow (-
>) member selection operators as follows:
// call setBalance via the Account object
account.setBalance( 123.45 );
// call setBalance via a reference to the Account object
accountRef.setBalance( 123.45 );
// call setBalance via a pointer to the Account object
accountPtr->setBalance( 123.45 );
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Access Functions
Access functions can read or display data.
A common use for access functions is to test the truth or falsity of
conditions—such functions are often called predicate functions.
Utility Functions
A utility function (also called a helper function) is a private member
function that supports the operation of the class’s other member functions.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
The program of Figs. 9.4–9.6 enhances class Time to demonstrate how
arguments are implicitly passed to a constructor.
The constructor defined in Fig. 9.2 initialized hour, minute and
second to 0 (i.e., midnight in universal time).
Like other functions, constructors can specify default arguments.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Notes Regarding Class Time’s Set and Get Functions and Constructor
Time’s set and get functions are called throughout the class’s body.
In each case, these functions could have accessed the class’s private data directly.
Consider changing the representation of the time from three int values (requiring 12 bytes of
memory on systems with four-byte ints) to a single int value representing the total number
of seconds that have elapsed since midnight (requiring only four bytes of memory).
If we made such a change, only the bodies of the functions that access the private data
directly would need to change.
◦ No need to modify the bodies of the other functions.
Designing the class in this manner reduces the likelihood of programming
errors when altering the class’s implementation.
Duplicating statements in multiple functions or constructors makes
changing the class’s internal data representation more difficult.
C++11: Using List Initializers to Call Constructors
C++11 now provides a uniform initialization syntax called list initializers that can be
used to initialize any variable. Lines 11–13 of Fig. 9.6 can be written using list
initializers as follows:
Time t2{ 2 }; // hour specified; minute and second defaulted
Time t3{ 21, 34 }; // hour and minute specified; second defaulted
Time t4{ 12, 25, 42 }; // hour, minute and second specified
or
Time t2 = { 2 }; // hour specified; minute and second defaulted
Time t3 = { 21, 34 }; // hour and minute specified; second defaulted
Time t4 = { 12, 25, 42 }; // hour, minute and second specified
The form without the = is preferred.
C++11: Overloaded Constructors and Delegating Constructors
A class’s constructors and member functions can also be overloaded.
Overloaded constructors typically allow objects to be initialized with different types
and/or numbers of arguments.
To overload a constructor, provide in the class definition a prototype for each version
of the constructor, and provide a separate constructor definition for each overloaded
version.
◦ This also applies to the class’s member functions.
In Figs. 9.4–9.6, the Time constructor with three parameters had a default argument for
each parameter. We could have defined that constructor instead as four overloaded
constructors with the following prototypes:
Time(); // default hour, minute and second to 0
Time( int ); // initialize hour; default minute and second to 0
Time( int, int); // initialize hour and minute; default second to 0
Time( int, int, int ); // initialize hour, minute and second
C++11 now allows constructors to call other constructors in the same class.
The calling constructor is known as a delegating constructor—it delegates its work to
another constructor.
The first three of the four Time constructors declared on the previous slide can
delegate work to one with three int arguments, passing 0 as the default value for
the extra parameters.
Use a member initializer with the name of the class as follows:
Time::Time()
Time( 0, 0, 0 ) //delegate to Time( int, int, int )
{
} // end constructor with no arguments
Time::Time( int hour )
Time( hour, 0, 0 ) //delegate to Time( int, int, int )
{
} // end constructor with one argument
Time::Time( int hour, int minute )
Time( hour, minute, 0 ) //delegate to Time( int, int, int )
{
} // end constructor with two arguments
The name of the destructor for a class is the tilde character (~) followed by the class
name.
Called implicitly when an object is destroyed.
The destructor itself does not actually release the object’s memory—it performs
termination housekeeping before the object’s memory is reclaimed, so the memory may
be reused to hold new objects.
Receives no parameters and returns no value.
May not specify a return type—not even void.
A class has one destructor.
A destructor must be public.
If you do not explicitly define a destructor, the compiler defines an “empty” destructor.
Constructors and destructors are called implicitly.
The order in which these function calls occur depends on the order in which
execution enters and leaves the scopes where the objects are instantiated.
Generally, destructor calls are made in the reverse order of the corresponding
constructor calls
◦ The storage classes of objects can alter the order in which destructors are called.
Constructors and Destructors for Objects in Global Scope
Constructors are called for objects defined in global scope (also called global
namespace scope) before any other function (including main) in that program
begins execution (although the order of execution of global object constructors
between files is not guaranteed).
◦ The corresponding destructors are called when main terminates.
Function exit forces a program to terminate immediately and does not execute the
destructors of local objects.
Function abort performs similarly to function exit but forces the program to
terminate immediately, without allowing the destructors of any objects to be called.
Constructors and Destructors for Local Objects
Constructors and destructors for local objects are called each time execution enters and
leaves the scope of the object.
Destructors are not called for local objects if the program terminates with a call to
function exit or function abort.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Constructors and Destructors for static Local Objects
The constructor for a static local object is called only once, when execution first
reaches the point where the object is defined—the corresponding destructor is called
when main terminates or the program calls function exit.
Global and static objects are destroyed in the reverse order of their creation.
Destructors are not called for static objects if the program terminates with a call to
function abort.
Demonstrating When Constructors and Destructors Are Called
The program of Figs. 9.7–9.9 demonstrates the order in which constructors and
destructors are called for objects of class CreateAndDestroy (Fig. 9.7 and Fig. 9.8)
of various storage classes in several scopes.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
A reference to an object is an alias for the name of the object and, hence, may be used on
the left side of an assignment statement.
In this context, the reference makes a perfectly acceptable lvalue that can receive a
value.
Unfortunately a public member function of a class can return a reference to a
private data member of that class.
Such a reference return actually makes a call to that member function an alias for the
private data member!
◦ The function call can be used in any way that the private data member can be used, including as
an lvalue in an assignment statement
◦ The same problem would occur if a pointer to the private data were to be returned by the
function.
If a function returns a reference that’s declared const, the reference is a non-modifiable
lvalue and cannot be used to modify the data.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
The program of Figs. 9.10–9.12 uses a simplified Time class (Fig. 9.10 and Fig. 9.11) to
demonstrate returning a reference to a private data member with member function
badSetHour.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
The assignment operator (=) can be used to assign an object to another object of the same
type.
By default, such assignment is performed by memberwise assignment (also called copy
assignment).
◦ Each data member of the object on the right of the assignment operator is assigned individually to the
same data member in the object on the left of the assignment operator.
[Caution: Memberwise assignment can cause serious problems when used with a class
whose data members contain pointers to dynamically allocated memory; we discuss these
problems in Chapter 10 and show how to deal with them.]
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Objects may be passed as function arguments and may be returned from
functions.
Such passing and returning is performed using pass-by-value by default—a copy
of the object is passed or returned.
◦ C++ creates a new object and uses a copy constructor to copy the original object’s values into
the new object.
For each class, the compiler provides a default copy constructor that copies each
member of the original object into the corresponding member of the new object.
◦ Copy constructors can cause serious problems when used with a class whose data members
contain pointers to dynamically allocated memory.
Chapter 10 discusses customized copy constructors.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Some objects need to be modifiable and some do not.
You may use keyword const to specify that an object is not modifiable and that
any attempt to modify the object should result in a compilation error.
The statement
const Time noon( 12, 0, 0 );
declares a const object noon of class Time and initializes it to 12 noon. It’s
possible to instantiate const and non- const objects of the same class.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
C++ disallows member function calls for const objects unless the member
functions themselves are also declared const.
This is true even for get member functions that do not modify the object.
This is also a key reason that we’ve declared as const all member-functions that do
not modify the objects on which they’re called.
A member function is specified as const both in its prototype by inserting the
keyword const after the function’s parameter list and, in the case of the function
definition, before the left brace that begins the function body.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
A constructor must be allowed to modify an object so that the object can be
initialized properly.
A destructor must be able to perform its termination housekeeping chores before an
object’s memory is reclaimed by the system.
Attempting to declare a constructor or destructor const is a compilation error.
The “constness” of a const object is enforced from the time the constructor
completes initialization of the object until that object’s destructor is called.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Using const and Non-const Member Functions
The program of Fig. 9.16 uses class Time from Figs. 9.4–9.5, but removes const from
function printStandard’s prototype and definition so that we can show a compilation
error.
An AlarmClock object needs to know when it’s supposed to sound its alarm,
so why not include a Time object as a member of the AlarmClock class?
Such a capability is called composition and is sometimes referred to as a has-a
relationship—a class can have objects of other classes as members.
The next program uses classes Date (Figs. 9.17–9.18) and Employee (Figs.
9.19–9.20) to demonstrate composition.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Employee Constructor’s Member Initializer List
The colon (:) following the constructor’s header (Fig. 9.20, line 12) begins the
member initializer list.
The member initializers specify the Employee constructor parameters being
passed to the constructors of the string and Date data members.
Again, member initializers are separated by commas.
The order of the member initializers does not matter.
They’re executed in the order that the member objects are declared in class
Employee.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Date Class’s Default Copy Constructor
As we mentioned in Section 9.9, the compiler provides each class with a default
copy constructor that copies each data member of the constructor’s argument
object into the corresponding member of the object being initialized.
Chapter 10 discusses how you can define customized copy constructors.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Testing Classes Date and Employee
Figure 9.21 creates two Date objects (lines 10–11) and passes them as arguments to the
constructor of the Employee object created in line 12.
Line 15 outputs the Employee object’s data.
When each Date object is created in lines 10–11, the Date constructor defined in lines 11–
25 of Fig. 9.18 displays a line of output to show that the constructor was called (see the first
two lines of the sample output).
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
[Note: Line 12 of Fig. 9.21 causes two additional Date constructor calls that do not
appear in the program’s output. When each of the Employee’s Date member objects
is initialized in the Employee constructor’s member-initializer list (Fig. 9.20, lines
14–15), the default copy constructor for class Date is called. Since this constructor is
defined implicitly by the compiler, it does not contain any output statements to
demonstrate when it’s called.]
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
What Happens When You Do Not Use the Member Initializer List?
If a member object is not initialized through a member initializer, the member
object’s default constructor will be called implicitly.
Values, if any, established by the default constructor can be overridden by set
functions.
However, for complex initialization, this approach may require significant additional
work and time.
A friend function of a class is a non-member function that has the right to access the
public and non-public class members.
Standalone functions, entire classes or member functions of other classes may be
declared to be friends of another class.
Declaring a friend
To declare a function as a friend of a class, precede the function prototype in the class definition
with keyword friend.
To declare all member functions of class ClassTwo as friends of class ClassOne, place a
declaration of the form
friend class ClassTwo;
in the definition of class ClassOne.
Friendship is granted, not taken—for class B to be a friend of class A, class A must explicitly
declare that class B is its friend.
Friendship is not symmetric—if class A is a friend of class B, you cannot infer that class B is a friend
of class A.
Friendship is not transitive—if class A is a friend of class B and class B is a friend of class C, you
cannot infer that class A is a friend of class C.
Modifying a Class’s private Data with a Friend Function
Figure 9.22 is a mechanical example in which we define friend function setX to set the private
data member x of class Count.
We place the friend declaration first in the class definition, even before public member functions
are declared.
Function setX is a stand-alone (global) function—it isn’t a member function of class Count.
For this reason, when setX is invoked for object counter, line 41 passes counter as an argument to
setX rather than using a handle (such as the name of the object) to call the function, as in
counter.setX( 8 ); // error: setX not a member function
If you remove the friend declaration in line 9, you’ll receive error messages indicating that function
setX cannot modify class Count’s private data member x.
It would normally be appropriate to define function setX as a member function of class Count.
It would also normally be appropriate to separate the program of Fig. 9.22 into three files:
1. A header (e.g., Count.h) containing the Count class definition, which in turn contains the
prototype of friend function setX
2. An implementation file (e.g., Count.cpp) containing the definitions of class Count’s member
functions and the definition of friend function setX
3. A test program (e.g., fig09_22.cpp) with main.
Overloaded friend Functions
It’s possible to specify overloaded functions as friends of a class.
Each function intended to be a friend must be explicitly declared in the class definition as a
friend of the class.
Every object has access to its own address through a pointer called this (a C++
keyword).
The this pointer is not part of the object itself—i.e., the memory occupied by the
this pointer is not reflected in the result of a sizeof operation on the object.
Rather, the this pointer is passed (by the compiler) as an implicit argument to each of
the object’s non-static member functions.
Using the this Pointer to Avoid Naming Collisions
Member functions use the this pointer implicitly (as we’ve done so far) or explicitly to reference an
object’s data members and other member functions.
A common explicit use of the this pointer is to avoid naming conflicts between a class’s data
members and member-function parameters (or other local variables).
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Consider the Time class’s hour data member and setHour member function in Figs. 9.4–9.5.
We could have defined setHour as:
// set hour value
void Time::setHour( int hour )
{
if ( hour >= 0 && hour < 24 )
this->hour + hour; //use this pointer to access data member
else
throw invalid_argument( "hour must be 0-23" );
} // end function setHour
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Type of the this Pointer
The type of the this pointer depends on the type of the object and whether the member function in
which this is used is declared const.
For example, in a non-const member function of class Employee, the this pointer has the type
Employee *. In a const member function, the this pointer has the type const Employee *.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Implicitly and Explicitly Using the this Pointer to Access an Object’s Data Members
Figure 9.23 demonstrates the implicit and explicit use of the this pointer to enable a member function
of class Test to print the private data x of a Test object.
In the next example and in Chapter 10, we show some substantial and subtle examples of using this.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Using the this Pointer to Enable Cascaded Function Calls
Another use of the this pointer is to enable cascaded member-function calls—that is, invoking
multiple functions in the same statement (as in line 12 of Fig. 9.26).
The program of Figs. 9.24–9.26 modifies class Time’s set functions setTime, setHour,
setMinute and setSecond such that each returns a reference to a Time object to enable
cascaded member-function calls.
Notice in Fig. 9.25 that the last statement in the body of each of these member functions returns
*this (lines 23, 34, 45 and 56) into a return type of Time &.
The program of Fig. 9.26 creates Time object t (line 9), then uses it in cascaded member-function
calls (lines 12 and 24).
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
In certain cases, only one copy of a variable should be shared by all objects of a class.
A static data member is used for these and other reasons.
Such a variable represents “class-wide” information, i.e., data that is shared by all instances and is not
specific to any one object of the class.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Scope and Initialization of static Data Members
static data members have class scope.
A static data member must be initialized exactly once.
Fundamental-type static data members are initialized by default to 0.
Prior to C++11, a static const data member of int or enum type could be initialized in its
declaration in the class definition and all other static data members had to be defined and
initialized at global namespace scope (i.e., outside the body of the class definition).
Again, C++11’s in-class initializers also allow you to initialize these variables where they’re declared
in the class definition.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Accessing static Data Members
A class’s private and protected static members are normally accessed through the class’s
public member functions or friends.
A class’s static members exist even when no objects of that class exist.
To access a public static class member when no objects of the class exist, simply prefix the
class name and the scope resolution operator (::) to the name of the data member.
To access a private or protected static class member when no objects of the class exist, provide a
public static member function and call the function by prefixing its name with the class name and
scope resolution operator.
A static member function is a service of the class, not of a specific object of the class.
©1992-2014 by Pearson Education, Inc. All Rights
Reserved.
Demonstrating static Data Members
The program of Figs. 9.27–9.29 demonstrates a private static data member
called count (Fig. 9.27, line 24) and a public static member function called
getCount (Fig. 9.27, line 18).