1. Trang chủ
  2. » Công Nghệ Thông Tin

Absolute C++ (4th Edition) part 49 doc

10 159 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 132,64 KB

Nội dung

Namespaces 487 Display 11.9 Hiding the Helping Functions in a Namespace (Implementation File) (part 3 of 3) 63 cout << "Error: illegal input to readHour\n"; 64 exit(1); 65 } 66 } 67 } //unnamed namespace 68 69 namespace DTimeSavitch 70 { 71 //Uses iostream: 72 istream& operator >>(istream& ins, DigitalTime& theObject) 73 { 74 readHour(theObject.hour); 75 readMinute(theObject.minute); 76 return ins; 77 } 78 ostream& operator <<(ostream& outs, const DigitalTime& theObject) < The body of the function definition is the same as in Display 11.2. > 79 bool operator ==(const DigitalTime& time1, const DigitalTime& time2) < The body of the function definition is the same as in Display 11.2. > 80 DigitalTime::DigitalTime(int theHour, int theMinute) < The body of the function definition is the same as in Display 11.2. > 81 DigitalTime::DigitalTime( ) < The body of the function definition is the same as in Display 11.2. > 82 int DigitalTime::getHour( ) const < The body of the function definition is the same as in Display 11.2. > 83 int DigitalTime::getMinute( ) const < The body of the function definition is the same as in Display 11.2. > 84 void DigitalTime::advance(int minutesAdded) < The body of the function definition is the same as in Display 11.2. > 85 void DigitalTime::advance(int hoursAdded, int minutesAdded) < The body of the function definition is the same as in Display 11.2. > 86 } //DTimeSavitch Within the compilation unit (in this case dtime.cpp), you can use names in the unnamed namespace without qualification. 488 Separate Compilation and Namespaces and implementation file for the class DigitalTime. Note that the helping functions readHour, readMinute, and digitToInt are all in the unnamed namespace; thus they are local to the compilation unit. As illustrated in Display 11.10, the names in the unnamed namespace can be reused for something else outside the compilation unit. In Display 11.10, the function name readHour is reused for a different function in the application program. Display 11.10 Hiding the Helping Functions in a Namespace (Application Program) (part 1 of 2) 1 //This is the application file timedemo.cpp. This program 2 //demonstrates hiding the helping functions in an unnamed namespace. 3 #include <iostream> 4 #include "dtime.h" 5 void readHour(int& theHour); 6 int main( ) 7 { 8 using std::cout; 9 using std::cin; 10 using std::endl; 11 using DTimeSavitch::DigitalTime; 12 int theHour; 13 readHour(theHour); 14 DigitalTime clock(theHour, 0), oldClock; 15 oldClock = clock; 16 clock.advance(15); 17 if (clock == oldClock) 18 cout << "Something is wrong."; 19 cout << "You entered " << oldClock << endl; 20 cout << "15 minutes later the time will be " 21 << clock << endl; 22 clock.advance(2, 15); 23 cout << "2 hours and 15 minutes after that\n" 24 << "the time will be " 25 << clock << endl; 26 return 0; 27 } If you place the using declarations here, then the program behavior will be the same. However, many authorities say that you should make the scope of each using declaration or using directive as small as is reasonable, and we wanted to give you an example of that technique. This is a different function readHour than the one in the implementation file dtime.cpp (shown in Display 11.9). Namespaces 489 If you look again at the implementation file Display 11.9, you will see that the help- ing functions digitToInt, readHour, and readMinute are used outside the unnamed namespace without any namespace qualifier. Any name defined in the unnamed name- space can be used without qualification anywhere in the compilation unit. (Of course, this needed to be so, since the unnamed namespace has no name to use for qualifying its names.) It is interesting to note how unnamed namespaces interact with the C++ rule that you cannot have two definitions of a name in the same namespace. There is one unnamed namespace in each compilation unit. It is easily possible for compilation units to overlap. For example, both the implementation file for a class and an applica- tion program using the class would normally both include the header file (interface file) for the class. Thus, the header file is in two compilation units and hence participates in two unnamed namespaces. As dangerous as this sounds, it will normally produce no problems as long as each compilation unit’s namespace makes sense when considered by itself. For example, if a name is defined in the unnamed namespace in the header Display 11.10 Hiding the Helping Functions in a Namespace (Application Program) (part 2 of 2) 28 29 void readHour(int& theHour) 30 { 31 using std::cout; 32 using std::cin; 33 34 cout << "Let's play a time game.\n" 35 << "Let's pretend the hour has just changed.\n" 36 << "You may write midnight as either 0 or 24,\n" 37 << "but, I will always write it as 0.\n" 38 << "Enter the hour as a number (0 to 24): "; 39 cin >> theHour; 40 } S AMPLE D IALOGUE Let's play a time game. Let's pretend the hour has just changed. You may write midnight as either 0 or 24, but, I will always write it as 0. Enter the hour as a number (0 to 24): 11 You entered 11:00 15 minutes later the time will be 11:15 2 hours and 15 minutes after that the time will be 13:30 When we gave these using declarations before, they were in main, so their scope was main. Thus, we need to repeat them here in order to use cin and cout in readHour. 490 Separate Compilation and Namespaces Pitfall file, it cannot be defined again in the unnamed namespace in either the implementa- tion file or the application file. Thus, a name conflict is avoided. C ONFUSING THE G LOBAL N AMESPACE AND THE U NNAMED N AMESPACE Do not confuse the global namespace with the unnamed namespace. If you do not put a name definition in a namespace grouping, then it is in the global namespace. To put a name definition in the unnamed namespace, you must put it in a namespace grouping that starts out as follows, without a name: namespace { Names in the global namespace and names in the unnamed namespace may both be accessed without a qualifier. However, names in the global namespace have global scope (all the program files), whereas names in an unnamed namespace are local to a compilation unit. This confusion between the global namespace and the unnamed namespace does not arise very much in writing code, since there is a tendency to think of names in the global namespace as being “in no namespace,” even though that is not technically correct. However, the confusion can easily arise when discussing code. U NNAMED N AMESPACE You can use the unnamed namespace to make a definition local to a compilation unit. Each com- pilation unit has one unnamed namespace. All the identifiers defined in the unnamed namespace are local to the compilation unit. You place a definition in the unnamed namespace by placing the definition in a namespace grouping with no namespace name, as shown below: namespace { Definition_1 Definition_2 . . . Definition_Last } You can use any name in the unnamed namespace without qualifiers anyplace in the compilation unit. See Displays 11.8, 11.9, and 11.10 for a complete example. Namespaces 491 Tip Tip U NNAMED N AMESPACES R EPLACE THE static Q UALIFIER Earlier versions of C++ used the qualifier static to make a name local to a file. This use of static is being phased out, and you should instead use the unnamed namespace to make a name local to a compilation unit. Note that this use of static has nothing to do with the use of static to qualify class members (as discussed in the subsection “Static Members” of Chapter 7 ). So, since static used to mean more than one thing, it is probably good that one use of the word is being phased out. H IDING H ELPING F UNCTIONS There are two good ways to hide a helping function for a class. You can make the function a private member function of the class or you can place the helping function in the unnamed namespace for the implementation file of the class. If the function naturally takes a calling object, then it should be made a private member function. If it does not naturally take a calling object, you can make it a static member function (for example, DigitalTime::readHour in Displays 11.1 and 11.2) or you can place it in the unnamed namespace of the implementation file (for exam- ple, readHour in Displays 11.8 and 11.9.) If the helping function does not need a calling object, then placing the helping function in the unnamed namespace of the implementation file makes for cleaner code because it better sepa- rates interface and implementation into separate files and it avoids the need for so much function name qualification. For example, note that in Display 11.9 we can use the function name read- Hour unqualified since it is in the unnamed namespace, while in the version in Display 11.2 we need to use DigitalTime::readHour. ■ NESTED NAMESPACES It is legal to nest namespaces. When qualifying a name from a nested namespace, you simply qualify twice. For example, consider namespace S1 { namespace S2 { void sample( ) { . . . } 492 Separate Compilation and Namespaces Tip . . . } //S2 }//S1 To invoke sample outside the namespace S1, you use S1::S2::sample( ); To invoke sample outside the namespace S2 but within namespace S1, you use S2::sample( ); Alternatively, you could use a suitable using directive. W HAT N AMESPACE S PECIFICATION S HOULD Y OU U SE ? You have three ways to specify that your code uses the definition of a function (or other item) named f that was defined in a namespace named theSpace. You can insert using namespace theSpace; Alternatively, you can insert using theSpace::f; Finally, you could omit the using directive altogether, but always qualify the function name by writing theSpace::f instead of just plain f. Which form should you use? All three methods can be made to work, and authorities differ on what they recommend as the preferred style. However, to obtain the full value of namespaces, it is good to avoid the form using namespace theSpace; Placing such a using directive at the start of a file is little different than placing all definitions in the global namespace, which is what earlier versions of C++ actually did. So, this approach gets no value from the namespace mechanism. (If you place such a using directive inside a block, how- ever, then it only applies to that block. This is another alternative, which is both sensible and advocated by many authorities.) We prefer to use the second method most of the time, inserting statements like the following at the start of files: using theSpace::f; Namespaces 493 Self-Test Exercises This allows you to omit names that are in the namespace but that are not used. That in turn avoids potential name conflicts. Moreover, it nicely documents which names you use, and it is not as messy as always qualifying a name with notation of the form theSpace::f. If your files are structured so that different namespaces are used in different locations, it may sometimes be preferable to place your using directives and using declarations inside blocks, such as the bodies of function definitions, rather than at the start of the file. 10. Would the program in Display 11.10 behave any differently if you replaced the four using declarations using std::cout; using std::cin; using std::endl; using DTimeSavitch::DigitalTime; with the following two using directives? using namespace std; using namespace DTimeSavitch; 11. What is the output produced by the following program? #include <iostream> using namespace std; namespace Sally { void message( ); } namespace { void message( ); } int main( ) { { message( ); using Sally::message; message( ); } 494 Separate Compilation and Namespaces message( ); return 0; } namespace Sally { void message( ) { cout << "Hello from Sally.\n"; } } namespace { void message( ) { cout << "Hello from unnamed.\n"; } } 12. What is the output produced by the following program? #include <iostream> using namespace std; namespace Outer { void message( ); namespace Inner { void message( ); } } int main( ) { Outer::message( ); Outer::Inner::message( ); using namespace Outer; Inner::message( ); return 0; } Answers to Self-Test Exercises 495 namespace Outer { void message( ) { cout << "Outer.\n"; } namespace Inner { void message( ) { cout << "Inner.\n"; } } } ■ You can define a class and place the definition of the class and the implementation of its member functions in separate files. You can then compile the class separately from any program that uses it, and you can use this same class in any number of different programs. ■ A namespace is a collection of name definitions, such as class definitions and vari- able declarations. ■ There are three ways to use a name from a namespace: by making all the names in the namespace available with a using directive, by making the single name available with a using declaration for the one name, or by qualifying the name with the name of the namespace and the scope resolution operator. ■ You place a definition in a namespace by placing the definition in a namespace grouping for that namespace. ■ The unnamed namespace can be used to make a name definition local to a compila- tion unit. ANSWERS TO SELF-TEST EXERCISES 1. Parts a, b, and c go in the interface file; parts d through h go in the implementation file. (All the definitions of class operations of any sort go in the implementation file.) Part i (that is, the main part of your program) goes in the application file. 2. The name of the interface file ends in .h. 3. Only the implementation file needs to be compiled. The interface file does not need to be compiled. Chapter Summary 496 Separate Compilation and Namespaces 4. Only the implementation file needs to be recompiled. You do, however, need to relink the files. 5. You need to delete the private member variables hour and minute from the interface file shown in Display 11.1 and replace them with the member variable minutes (with an s). You do not need to make any other changes in the interface file. In the implementation file, you need to change the definitions of all the constructors and other member functions, as well as the definitions of the overloaded operators, so that they work for this new way of recording time. (In this case, you do not need to change any of the helping functions readHour, readMinute, or digitToInt, but that might not be true for some other class or even some other reimplementation of this class.) For example, the definition of the over- loaded operator, >>, could be changed to the following: istream& operator >>(istream& ins, DigitalTime& theObject) { int inputHour, inputMinute; DigitalTime::readHour(inputHour); DigitalTime::readMinute(inputMinute); theObject.minutes = inputMinute + 60*inputHour; return ins; } You need not change any application files for programs that use the class. However, since the interface file is changed (as well as the implementation file), you will need to recompile any application files, and of course you will need to recompile the implementation file. 6. No. If you replace bigGreeting with greeting, you will have a definition for the name greeting in the global namespace. There are parts of the program where all the name def- initions in the namespace Space1 and all the name definitions in the global namespace are simultaneously available. In those parts of the program, there would be two distinct defini- tions for void greeting( ); 7. Yes. The additional definition would cause no problems because overloading is always allowed. When, for example, the namespace Space1 and the global namespace are avail- able, the function name greeting would be overloaded. The problem in Self-Test Exer- cise 6 was that there would sometimes be two definitions of the function name greeting with the same parameter lists. 8. Hello Good-Bye Good-Bye 9. void wow(speedway::speed s1, indy500::speed s2); . EXERCISES 1. Parts a, b, and c go in the interface file; parts d through h go in the implementation file. (All the definitions of class operations of any sort go in the implementation file.) Part i. theSpace::f; Namespaces 493 Self-Test Exercises This allows you to omit names that are in the namespace but that are not used. That in turn avoids potential name conflicts. Moreover, it nicely documents which. 11.9, and 11.10 for a complete example. Namespaces 491 Tip Tip U NNAMED N AMESPACES R EPLACE THE static Q UALIFIER Earlier versions of C++ used the qualifier static to make a name local

Ngày đăng: 04/07/2014, 05:21

TỪ KHÓA LIÊN QUAN