11 Separate Compilation and Namespaces11.1 SEPARATE COMPILATION 458 Encapsulation Reviewed 459 Header Files and Implementation Files 460 Example: DigitalTime Class 468 Tip: Reusable Comp
Trang 111 Separate Compilation and Namespaces
11.1 SEPARATE COMPILATION 458
Encapsulation Reviewed 459 Header Files and Implementation Files 460 Example: DigitalTime Class 468 Tip: Reusable Components 469 Using #ifndef 469
Tip: Defining Other Libraries 472
11.2 NAMESPACES 473
Namespaces and using Directives 473 Creating a Namespace 475
Using Declarations 478 Qualifying Names 480 Example: A Class Definition in a Namespace 482 Tip: Choosing a Name for a Namespace 482 Unnamed Namespaces 484
Pitfall: Confusing the Global Namespace and the Unnamed Namespace 490
Tip: Unnamed Namespaces Replace the static Qualifier 491 Tip: Hiding Helping Functions 491
Nested Namespaces 491 Tip: What Namespace Specification Should You Use? 492
CHAPTER SUMMARY 495 ANSWERS TO SELF-TEST EXERCISES 495 PROGRAMMING PROJECTS 497
Trang 211 Separate Compilation and Namespaces
From mine own library with volumes that
I prize above my dukedom.
William Shakespeare, The Tempest
INTRODUCTION
This chapter covers two topics that have to do with how to organize a C++ program into separate parts Section 11.1 on separate compilation discusses how a C++ program can be distributed across a number of files so that when some parts of the program change only those parts need to be recompiled and
so that the separate parts can be more easily reused in other applications Section 11.2 discusses namespaces, which were introduced briefly in Chap-ter 1 Namespaces are a way of allowing you to reuse the names of classes, functions, and other items by qualifying the names to indicate different uses Namespaces divide your code into sections so that the different sections may reuse the same names with differing meanings They allow a kind of local meaning for names that is more general than local variables
This chapter can be covered earlier than its location in the book This chap-ter does not use any of the machap-terial from Chapchap-ters 5 (arrays), 9 (strings), 10 (pointers and dynamic arrays) or Section 7.3 (vectors) of Chapter 7
Separate Compilation
Your “if ” is the only peacemaker; much virtue in “if.”
William Shakespeare, As You Like It
C++ has facilities for dividing a program into parts that are kept in separate files, compiled separately, and then linked together when (or just before) the program is run You can place the definition for a class (and its associated function definitions) in files that are separate from the programs that use the class In this way you can build up a library of classes so that many programs can use the same class You can compile the class once and then use it in many different programs, just like you use the predefined libraries such as those with header files iostream and cstdlib Moreover, you can define the class itself in two files so that the specification of what the class does is separate from how the class is implemented If you only change the implementation of the class,
11.1
Trang 3Separate Compilation 459
then you need only recompile the file containing the class implementation The other
files, including the files containing the programs that use the class, need not be changed
or even recompiled This section tells you how to carry out this separate compilation of
classes
■ ENCAPSULATION REVIEWED
The principle of encapsulation says that you should separate the specification of how
the class is used by a programmer from the details of how the class is implemented The
separation should be so complete that you can change the implementation without
needing to change any program that uses the class The way to ensure this separation
can be summarized in three rules:
1 Make all the member variables private members of the class
2 Make each of the basic operations for the class either a public member function of
the class, a friend function, an ordinary function, or an overloaded operator Group
the class definition and the function and operator declarations (prototypes) together
This group, along with its accompanying comments, is called the interface for the
class Fully specify how to use each such function or operator in a comment given
with the class or with the function or operator declaration
3 Make the implementation of the basic operations unavailable to the programmer
who uses the class The implementation consists of the function definitions and
overloaded operator definitions (along with any helping functions or other
addi-tional items these definitions require)
In C++, the best way to ensure that you follow these rules is to place the interface
and the implementation of the class in separate files As you might guess, the file that
contains the interface is often called the interface file, and the file that contains the
implementation is called the implementation file The exact details of how to set up,
compile, and use these files will vary slightly from one version of C++ to another, but
the basic scheme is the same in all versions of C++ In particular, the details of what
goes into the files is the same in all systems The only things that vary are the
com-mands used to compile and link these files The details about what goes into these files
are illustrated in the next subsection
A typical class has private member variables Private member variables (and private
member functions) present a problem to our basic philosophy of placing the interface
and the implementation of a class in separate files The public part of the definition for
a class is part of the interface for the class, but the private part is part of the
implemen-tation This is a problem because C++ will not allow you to split the class definition
across two files Thus, some sort of compromise is needed The only sensible
compro-mise is to place the entire class definition in the interface file Since a programmer who
is using the class cannot use any of the private members of the class, the private
mem-bers will, in effect, still be hidden from the programmer
interface
implemen-tation
interface file and implemen-tation file
Private members are part of the imple-mentation.
Trang 4460 Separate Compilation and Namespaces
■ HEADER FILES AND IMPLEMENTATION FILES
Display 11.1 contains the interface file for a class called DigitalTime DigitalTime is a class whose values are times of day, such as 9:30 Only the public members of the class are part of the interface The private members are part of the implementation, even though they are in the interface file The label private: warns you that these private members are not part of the public interface Everything that a programmer needs to know in order to use the class DigitalTime is explained in the comment at the start of the file and in the comments in the public section of the class definition As noted in the comment at the top of the interface file, this class uses 24-hour notation, so, for instance, 1:30 P.M is input and output as 13:30 This and the other details you must know in order to effectively use the class DigitalTime are included in the comments given with the member functions
We have placed the interface in a file named dtime.h The suffix .h indicates that this is a header file An interface file is always a header file and so always ends with the suffix .h Any program that uses the class DigitalTime must contain an include direc-tive like the following, which names this file:
#include "dtime.h"
When you write an include directive, you must indicate whether the header file is a predefined header file that is provided for you or is a header file that you wrote If the header file is predefined, write the header file name in angular brackets, like <ios-tream> If the header file is one that you wrote, write the header file name in quotes, like "dtime.h" This distinction tells the compiler where to look for the header file If the header file name is in angular brackets, the compiler looks wherever the predefined header files are kept in your implementation of C++ If the header file name is in quotes, the compiler looks in the current directory or wherever programmer-defined header files are kept on your system
Any program that uses our DigitalTime class must contain the above include direc-tive that names the header file dtime.h That is enough to allow you to compile the program, but is not enough to allow you to run the program In order to run the pro-gram you must write (and compile) the definitions of the member functions and the overloaded operators We have placed these function and operator definitions in another file, which is called the implementation file Although it is not required by most compilers, it is traditional to give the interface file and the implementation file the same name The two files do, however, end in different suffixes We have placed the interface for our class in the file named dtime.h and the implementation for our class
in a file named dtime.cpp The suffix you use for the implementation file depends on your version of C++ Use the same suffix for the implementation file as you normally use for files that contain C++ programs (Other common suffixes are .cxx and .hxx.) The implementation file for our DigitalTime class is given in Display 11.2 After we explain how the various files for our class interact with each other, we will return to Display 11.2 and discuss the details of the definitions in this implementation file
header files
include
file names
Trang 5Separate Compilation 461
Display 11.1 Interface File for the DigitalTime Class
1 //This is the header file dtime.h This is the interface for the class DigitalTime.
2 //Values of this type are times of day The values are input and output in 24-hour
3 //notation, as in 9:30 for 9:30 AM and 14:45 for 2:45 PM.
4 #include <iostream>
5 using namespace std;
6 class DigitalTime
7 {
8 public :
9 DigitalTime( int theHour, int theMinute);
10 DigitalTime( );
11 //Initializes the time value to 0:00 (which is midnight).
12 getHour( ) const ;
13 getMinute( ) const ;
14 void advance( int minutesAdded);
15 //Changes the time to minutesAdded minutes later.
16 void advance( int hoursAdded, int minutesAdded);
17 //Changes the time to hoursAdded hours plus minutesAdded minutes later.
18 friend bool operator ==( const DigitalTime& time1,
19 const DigitalTime& time2);
20 friend istream& operator >>(istream& ins, DigitalTime& theObject);
21 friend ostream& operator <<(ostream& outs, const DigitalTime& theObject);
22 private :
23 int hour;
24 int minute;
25 static void readHour( int & theHour);
26 //Precondition: Next input to be read from the keyboard is
27 //a time in notation, like 9:45 or 14:45.
28 //Postcondition: theHour has been set to the hour part of the time
29 //The colon has been discarded and the next input to be read is the minute.
30 static void readMinute( int & theMinute);
31 //Reads the minute from the keyboard after readHour has read the hour.
32 static int digitToInt( char c);
33 //Precondition: c is one of the digits ’0’ through ’9’.
34 //Returns the integer for the digit; for example, digitToInt(’3’) returns 3 35
36 };
These member variables and helping functions are part of the implementation They are not part
of the interface The word private indicates that they are not part of the public interface.
Trang 6462 Separate Compilation and Namespaces
Display 11.2 Implementation File (part 1 of 3)
1 //This is the implementation file dtime.cpp of the class DigitalTime.
2 //The interface for the class DigitalTime is in the header file dtime.h.
3 #include <iostream>
4 #include <cctype>
5 #include <cstdlib>
6 using namespace std;
7 #include "dtime.h"
8 //Uses iostream and cstdlib:
9 DigitalTime::DigitalTime( int theHour, int theMinute)
10 {
11 if (theHour < 0 || theHour > 24 || theMinute < 0 || theMinute > 59)
12 {
13 cout << "Illegal argument to DigitalTime constructor.";
14 exit(1);
15 }
16 else
17 {
20 }
21 if (hour == 24)
22 hour = 0; //Standardize midnight as 0:00
23 }
24 DigitalTime::DigitalTime( )
25 {
27 minute = 0;
28 }
29 int DigitalTime::getHour( ) const
30 {
31 return hour;
32 }
33
34 int DigitalTime::getMinute( ) const
35 {
36 return minute;
37 }
38 void DigitalTime::advance( int minutesAdded)
39 {
40 int grossMinutes = minute + minutesAdded;
41 minute = grossMinutes%60;
42 int hourAdjustment = grossMinutes/60;
Trang 7Separate Compilation 463
Display 11.2 Implementation File (part 2 of 3)
43 hour = (hour + hourAdjustment)%24;
44 }
45 void DigitalTime::advance( int hoursAdded, int minutesAdded)
46 {
47 hour = (hour + hoursAdded)%24;
48 advance(minutesAdded);
49 }
50 bool operator ==( const DigitalTime& time1, const DigitalTime& time2)
51 {
52 return (time1.hour == time2.hour && time1.minute == time2.minute);
53 }
54 //Uses iostream:
55 ostream& operator <<(ostream& outs, const DigitalTime& theObject)
56 {
57 outs << theObject.hour << ’:’;
58 if (theObject.minute < 10)
59 outs << ’0’;
60 outs << theObject.minute;
61 return outs;
62 }
63
64 //Uses iostream:
65 istream& operator >>(istream& ins, DigitalTime& theObject)
66 {
67 DigitalTime::readHour(theObject.hour);
68 DigitalTime::readMinute(theObject.minute);
69 return ins;
70 }
71 int DigitalTime::digitToInt( char c)
72 {
73 return ( int (c) - int (’0’) );
74 }
75 //Uses iostream, cctype, and cstdlib:
76 void DigitalTime::readMinute(int& theMinute)
77 {
78 char c1, c2;
79 cin >> c1 >> c2;
80 if (!(isdigit(c1) && isdigit(c2)))
81 {
82 cout << "Error: illegal input to readMinute\n";
84 }
Trang 8464 Separate Compilation and Namespaces
Display 11.2 Implementation File (part 3 of 3)
85 theMinute = digitToInt(c1)*10 + digitToInt(c2);
86 if (theMinute < 0 || theMinute > 59)
87 {
88 cout << "Error: illegal input to readMinute\n";
90 }
91 }
92
93 //Uses iostream, cctype, and cstdlib:
94 void DigitalTime::readHour( int & theHour)
95 {
96 char c1, c2;
97 cin >> c1 >> c2;
98 if ( !( isdigit(c1) && (isdigit(c2) || c2 == ’:’ ) ) )
99 {
100 cout << "Error: illegal input to readHour\n";
102 }
103 if (isdigit(c1) && c2 == ’:’)
104 {
105 theHour = DigitalTime::digitToInt(c1);
106 }
107 else //(isdigit(c1) && isdigit(c2))
108 {
109 theHour = DigitalTime::digitToInt(c1)*10
110 + DigitalTime::digitToInt(c2);
111 cin >> c2; //discard ’:’
113 {
114 cout << "Error: illegal input to readHour\n";
115 exit(1);
117 }
118 if (theHour == 24)
119 theHour = 0; //Standardize midnight as 0:00
120 if ( theHour < 0 || theHour > 23 )
121 {
122 cout << "Error: illegal input to readHour\n";
124 }
125 }
Trang 9Separate Compilation 465
Any file that uses the class DigitalTime must contain the include directive
#include "dtime.h"
Thus, both the implementation file and the program file must contain the include
directive that names the interface file The file that contains the program (that is, the
file that contains the main function) is often called the application file or driver file.
Display 11.3 contains an application file with a very simple program that uses and
demonstrates the DigitalTime class
The exact details of how to run this complete program, which is contained in three
files, depend on what system you are using However, the basic details are the same for
all systems You must compile the implementation file and you must compile the
appli-cation file that contains the main function You do not compile the interface file, which
in this example is the file dtime.h given in Display 11.1 You do not need to compile
the interface file because the compiler thinks the contents of this interface file are
already contained in each of the other two files Recall that both the implementation
file and the application file contain the directive
#include "dtime.h"
Compiling your program automatically invokes a preprocessor that reads this include
directive and replaces it with the text in the file dtime.h Thus, the compiler sees the
contents of dtime.h, and so the file dtime.h does not need to be compiled separately
(In fact, the compiler sees the contents of dtime.h twice: once when you compile the
implementation file and once when you compile the application file.) This copying of
the file dtime.h is only a conceptual copying The compiler acts as if the contents of
dtime.h were copied into each file that has the include directive However, if you look
in those files after they are compiled, you will only find the include directive; you will
not find the contents of the file dtime.h
Once the implementation file and the application file are compiled, you still need to
connect these files so that they can work together This is called linking the files and is
done by a separate utility called a linker The details of how to call the linker depends
on what system you are using Often, the command to run a program automatically
invokes the linker, so you need not explicitly call the linker at all After the files are
linked, you can run your program
This sounds like a complicated process, but many systems have facilities that
man-age much of this detail for you automatically or semiautomatically On any system, the
details quickly become routine On UNIX systems, these details are handled by the
make facility In most IDEs (Integrated Development Environments) these various
files are combined into something called a project.
Displays 11.1, 11.2, and 11.3 contain one complete program divided into pieces
and placed in three different files You could instead combine the contents of these
three files into one file, and then compile and run this one file without all this fuss
application file or driver file
compiling and running the program
linking linker
make project Why separate files?
Trang 10466 Separate Compilation and Namespaces
Display 11.3 Application File Using DigitalTime Class
1 //This is the application file timedemo.cpp, which demonstrates use of DigitalTime.
2 #include <iostream>
3 using namespace std;
4 #include "dtime.h"
5 int main( )
6 {
7 DigitalTime clock, oldClock;
8 cout << "You may write midnight as either 0:00 or 24:00,\n"
9 << "but I will always write it as 0:00.\n"
10 << "Enter the time in 24-hour notation: ";
11 cin >> clock;
12 oldClock = clock;
13 clock.advance(15);
14 if (clock == oldClock)
15 cout << "Something is wrong.";
16 cout << "You entered " << oldClock << endl;
17 cout << "15 minutes later the time will be "
18 << clock << endl;
19 clock.advance(2, 15);
20 cout << "2 hours and 15 minutes after that\n"
21 << "the time will be "
22 << clock << endl;
23 return 0;
24 }
S AMPLE D IALOGUE
You may write midnight as either 0:00 or 24:00,
but I will always write it as 0:00.
Enter the time in 24-hour notation: 11:15
You entered 11:15
15 minutes later the time will be 11:30
2 hours and 15 minutes after that
the time will be 13:45