Time
Class Case Study: Separating Interface from ImplementationPlacing a complete class definition in a header reveals the entire implementation of the class to the class’s clients—a header is simply a text file that anyone can open and read.
Conventional software engineering wisdom says that to use an object of a class, the client code needs to know only: what member functions to call, what arguments to provide to each member function and what return type to expect from each member function. The client code does not need to know how those functions are implemented.
Interfaces (p. 388) define and standardize the ways in which things such as people and systems interact with one another.
The interface of a class describes what services a class’s clients can use and how to request those services, but not how the class carries out the services.
A class’s public
interface consists of the class’s public
member functions (also known as the class’s public
services).
You can specify a class’s interface by writing a class definition that lists only the class’s member-function prototypes and the class’s data members.
To separate the class’s interface from its implementation, we break up the class into two files— a header in which the class is defined and a source-code file in which the class’s member functions are defined.
By convention, member-function definitions are placed in a source-code file of the same base name as the class’s header but with a .cpp
filename extension (some compilers support other filename extensions as well).
Time
Class DefinitionA class definition that contains function prototypes rather than definitions describes the class’s public
interface without revealing the class’s member-function implementations.
The class’s header still specifies the class’s private
data members as well—the compiler must know the data members of the class to determine how much memory to reserve for each object of the class.
Including the class’s header in the client code provides the compiler with the information it needs to ensure that the client code calls the class’s member functions correctly.
An include guard (p. 389)—consisting of #ifndef
, #define
and #endif
—prevents a header from being #include
d multiple times in the same source-code file.
Attempts to include a header multiple times (inadvertently) typically occur in large programs with many headers that may themselves include other headers.
::
)When member functions are defined outside a class’s definition, each member function’s name must be preceded by the class name and the scope resolution operator (::
). This “ties” each member function to the class definition, which declares the class’s members, telling the compiler that each member function is within that class’s scope (p. 391).
The source-code file containing a class’s member-function definitions must include the class’s header. This enables the compiler to ensure that the first line of each member function matches its prototype in the class’s header and that each member function knows about the class’s data members and other member functions.
Time
Class Member Function setTime
and Throwing ExceptionsA function can throw an exception (p. 392) of type invalid_argument
(p. 392) to notify the client code that an invalid argument was received.
A throw
statement (p. 392) creates and throws an object of the type specified to the right of the throw
keyword.
After an exception object is created, the throw
statement immediately terminates the function and the exception is returned to the calling function.
Time
Class Member Function toUniversalString
and String Stream ProcessingObjects of class ostringstream
(p. 392; from the header <sstream>
) provide the same functionality as cout
, but write their output to string
objects in memory.
Class ostringstream
’s str
(p. 392) member function returns the string
created by an ostring-stream
.
Parameterized stream manipulator setfill
(p. 392) specifies the fill character (p. 392) that’s 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 for a right-aligned value—for left aligned values, the fill characters appear to the right.
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—this is a sticky setting.
A member function defined in a class’s body is implicitly declared inline.
Classes often contain member functions that take no arguments, because these member functions implicitly know the data members for the particular object on which they’re invoked. This can make member-function calls more concise and less error prone than conventional function calls in procedural programming.
Time
Once a class is defined, it can be used as a type in declarations of objects, references and pointers.
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; physically, however, this is not the case—member functions are stored separately from the objects of a class.
Often a class’s interface and implementation will be created and compiled by one programmer and used by a separate programmer who implements the client code that uses the class.
A class-implementation programmer responsible for creating a reusable class creates the header and the source-code file that #include
s the header, then compiles the source-code file to create the class’s object code.
To hide the class’s member-function implementation details, the class-implementation programmer would provide the client-code programmer with the class’s header (which specifies the class’s interface and data members) and the class’s object code (i.e., the machine-code instructions that represent the class’s member functions).
The client-code programmer is not given the source-code file with the class’s member function definitions, so the client remains unaware of how the class’s member functions are implemented.
The client-code programmer needs to know only the class’s interface to use the class and must be able to link its object code. Since the interface of the class is part of the class definition in the class’s header, the client-code programmer must have access to this file and must #include
it in the client’s source-code file.
To create the executable application, the last step is to link the object code for the client code, the object code for the member-function implementations of the class(es) used by the client code and the C++ Standard Library object code used by the class-implementation programmer and the client-code programmer.
The linker’s output is the executable application. Compilers and IDEs typically invoke the linker for you after compiling your code.
A class’s data members and member functions belong to that class’s scope.
Nonmember functions are defined at global namespace scope.
Within a class’s scope, class members are immediately accessible by all of that class’s member non-static
functions and can be referenced by name.
Outside a class’s scope, 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.
Variables declared in a member function have block scope and are known only to that function.
The dot member selection operator (.
) is preceded by an object’s name or by a reference to an object to access the object’s public
members.
The arrow member selection operator (->
;
p. 398) is preceded by a pointer to an object to access that object’s public
members.
Access functions (p. 399) read or display data. They can also be used to test the truth or falsity of conditions—such functions are often called predicate functions.
A utility function (p. 399) is a private
member function that supports the operation of the class’s public
member functions. Utility functions are not intended to be used by clients of a class.
Like other functions, constructors can specify default 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.
Just as a constructor can call a class’s other member functions to perform tasks, C++11 allows constructors to call other constructors in the same class. To do so, you use a member initializer with the name of the class.
The calling constructor is known as a delegating constructor (p. 405)—it delegates its work to another constructor. This is useful when overloaded constructors have common code that previously would have been defined in a private
utility function and called by all the constructors.
A class’s destructor (p. 405) is called implicitly when an object of the class is destroyed.
The name of the destructor for a class is the tilde (~
) character followed by the class name.
A destructor does not release an object’s storage—it performs termination housekeeping (p. 405) before the system reclaims an object’s memory, so the memory may be reused to hold new objects.
A destructor receives no parameters and returns no value. A class may have only one destructor.
If you do not explicitly provide a destructor, the compiler creates an “empty” destructor, so every class has exactly one destructor.
The order in which constructors and destructors are called 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, but the global and local static
objects’ destructors are called after all non-static
local objects are destroyed.
Function exit
forces a program to terminate immediately and does not execute the destructors of local objects. exit
often is used to terminate a program when a fatal unrecoverable error occurs.
Function abort
forces the program to terminate immediately, without allowing programmer-defined cleanup code of any kind to be called. abort
is usually used to indicate an abnormal termination of the program.
Time
Class Case Study: A Subtle Trap—Returning a Reference or a Pointer to a private
Data MemberA 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.
If the function returns a reference to const
data, then the reference cannot be used as a modifiable lvalue.
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 (p. 411).
Objects may be passed by value to or returned by value from functions. C++ creates a new object and uses a copy constructor (p. 413) 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.
const
Objects and const
Member FunctionsThe keyword const
can be used to specify that an object is not modifiable and that any attempt to modify the object should result in a compilation error.
C++ compilers disallow non-const
member function calls on const
objects.
An attempt by a const
member function to modify an object of its class is a compilation error.
A member function is specified as const
both in its prototype and in its definition.
A const
object must be initialized.
Constructors and destructors cannot be declared const
.
A class can have objects of other classes as members—this concept is called composition.
Member objects are constructed in the order in which they’re declared in the class definition and before their enclosing class objects are constructed.
If a member initializer is not provided for a member object, the member object’s default constructor will be called implicitly.
friend
Functions and friend
ClassesA friend
function (p. 421) of a class is defined outside that class’s scope, yet has the right to access all of the class’s members. Standalone functions or entire classes may be declared to be friend
s.
A friend
declaration can appear anywhere in the class.
The friendship relation is neither symmetric nor transitive.
this
PointerEvery object has access to its own address through the this
pointer (p. 423).
An object’s this
pointer is not part of the object itself—i.e., the size of the memory occupied by the this
pointer is not reflected in the result of a sizeof
operation on the object.
The this
pointer is passed as an implicit argument to each non-static
member function.
Objects use the this
pointer implicitly (as we’ve done to this point) or explicitly to reference their data members and member functions.
The this
pointer enables cascaded member-function calls (p. 425) in which multiple functions are invoked in the same statement.
static
Class MembersA static
data member (p. 429) represents “classwide” information (i.e., a property of the class shared by all instances, not a property of a specific object of the class).
static
data members have class scope and can be declared public
, private
or protected
.
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.
The static
keyword cannot be applied to a member definition that appears outside the class definition.
A member function should be declared static
(p. 430) if it does not access non-static
data members or non-static
member functions of the class. Unlike non-static
member functions, a static
member function does not have a this
pointer, because static
data members and static
member functions exist independently of any objects of a class.