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

C++ Weekend Crash Course phần 8 docx

51 238 0

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

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 51
Dung lượng 347,1 KB

Nội dung

#include <stdio.h> #include <iostream.h> class Furniture { public: Furniture() { cout << “Creating the furniture concept”; } int weight; }; class Bed : virtual public Furniture { public: Bed() { cout << “Building the bed part\n”; } void sleep() { cout << “Trying to get some sleep over here!\n”; } }; class Sofa : virtual public Furniture { public: Sofa() { cout << “Building the sofa part\n”; } void watchTV() { cout << “Watching TV\n”; } }; //SleeperSofa - is both a Bed and a Sofa Sunday Morning354 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 354 class SleeperSofa : public Bed, public Sofa { public: // the constructor doesn’t need to do anything SleeperSofa() { cout << “Putting the two together\n”; } void foldOut() { cout << “Folding the bed out\n”; } }; int main() { // output the weight of the sleepersofa SleeperSofa ss; cout << “sofa weight = “ << ss.weight //this doesn’t work! // << ss.Sofa::weight // this does work << “\n”; return 0; } Notice the addition of the keyword virtual in the inheritance of Furniture in Bed and Sofa . This says, “Give me a copy of Furniture unless you already have one somehow, in which case I’ll just use that one.” A SleeperSofa ends up looking like Figure 24-5 in memory. Figure 24-5 Memory layout of SleeperSofa with virtual inheritance Furniture Bed stuff SleeperSofa Sofa stuff SleeperSofa stuff Session 24—Multiple Inheritance 355 Part V–Sunday Morning Session 24 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 355 A SleeperSofa inherits Furniture , then Bed minus the Furniture part, followed by Sofa minus the Furniture part. Bringing up the rear are the members unique to SleeperSofa . (This may not be the order of the elements in memory, but that’s not important for our purposes.) The reference in main() to weight is no longer ambiguous because a SleeperSofa contains only one copy of Furniture . By inheriting Furniture virtually, you get the desired inheritance relationship as expressed in Figure 24-2. If virtual inheritance solves this problem so nicely, why isn’t it the norm? There are two reasons. First, virtually inherited base classes are handled internally much differently than normally inherited base classes, and these differences involve extra overhead. (Not that much extra overhead, but the makers of C++ were almost obses- sively paranoid about overhead.) Second, sometimes you want two copies of the base class (although this is unusual). I think virtual inheritance should be the norm. As an example of a case in which you might not want virtual inheritance, con- sider a TeacherAssistant who is both a Student and a Teacher , both of which are subclasses of Academician . If the university gives its teaching assistants two IDs — a student ID and a separate teacher ID — class TeacherAssistant will need to contain two copies of class Academician . Constructing the Objects of Multiple Inheritance The rules for constructing objects need to be expanded to handle multiple inheritance. The constructors are invoked in this order: ¼ First, the constructor for any virtual base classes is called in the order in which the classes are inherited. ¼ Then the constructor for any nonvirtual base class is called in the order in which the classes are inherited. ¼ Next, the constructor for any member objects is called in the order in which the member objects appear in the class. ¼ Finally, the constructor for the class itself is called. Base classes are constructed in the order in which they are inherited and not in the order in which they appear on the constructor line. Note Sunday Morning356 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 356 A Contrary Opinion Not all object-oriented practitioners think that multiple inheritance is a good idea. In addition, many object-oriented languages don’t support multiple inheritance. For example, Java does not support multiple inheritance—it is considered too dangerous and not really worth the trouble. Multiple inheritance is not an easy thing for the language to implement. This is mostly the compiler’s problem (or the compiler writer’s problem) and not the programmer’s problem. However, multiple inheritance opens the door to addi- tional errors. First, there are the ambiguities mentioned in the section “Inheritance Ambiguities.” Second, in the presence of multiple inheritance, casting a pointer from a subclass to a base class often involves changing the value of the pointer in sophis- ticated and mysterious ways, which can result in unexpected results. For example: #include <iostream.h> class Base1 {int mem;}; class Base2 {int mem;}; class SubClass : public Base1, public Base2 {}; void fn(SubClass *pSC) { Base1 *pB1 = (Base1*)pSC; Base2 *pB2 = (Base2*)pSC; if ((void*)pB1 == (void*)pB2) { cout << “Members numerically equal\n”; } } int main() { SubClass sc; fn(&sc); return 0; } pB1 and pB2 are not numerically equal even though they came from the same original value, pSC , and the message “Members numerically equal” doesn’t appear. (Actually, fn() is passed a zero because C++ doesn’t perform these transmigrations on null . See how strange it gets?) Session 24—Multiple Inheritance 357 Part V–Sunday Morning Session 24 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 357 REVIEW I suggest that you avoid using multiple inheritance until you are comfortable with C++. Single inheritance provides enough expressive power to get used to. Later, you can study the manuals until you’re sure that you understand exactly what’s going on when you use multiple inheritance. One exception is the use of commercial libraries such as Microsoft’s Foundation Classes (MFC), which use multiple inheritance quite a bit. These classes have been checked out and are safe. (You are generally not even aware that you are using multiply inherited base classes when using libraries such as MFC.) ¼ A class can inherit from more than one class by stringing class names, sepa- rated by commas, after the : . Although the examples in this base class only used two base classes, there is no reasonable limitation to the number of base classes. Inheritance from more than two base classes is extremely unusual. ¼ Members that the base classes share are ambiguous to the subclass. That is, if both BaseClass1 and BaseClass2 contain a member function f() , then f() is ambiguous in Subclass . ¼ Ambiguities in the base classes can be resolved by using a class indicator, thus the subclass might refer to BaseClass1::f() and BaseClass2::f() . ¼ Having both base classes inherit from a common base class of their own in which common properties have been factored out can solve the problem if the classes inherit virtually. QUIZ YOURSELF 1. What might we use as the base classes for a class like CombinationPrinterCopier ? (A printer-copier is a laser printer that can also serve as a copy machine.) (See the introduction section.) 2. Complete the following class description by replacing the question marks: class Printer { public: int nVoltage; // other stuff } class Copier Sunday Morning358 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 358 { public: int nVolatage; // other stuff } class CombinationPinterCopier ????? { // other stuff } 3. What is the main problem that might arise in accessing the voltage of a CombinationPrinterCopier object? (See “Inheritance Ambiguities.”) 4. Given that both a Printer and a Copier are ElectronicEquipment , what could be done to solve the voltage problem? (See “Virtual Inheritance.”) 5. What are some of the reasons why multiple inheritance might not be a good thing? (See “A Contrary Opinion.”) Session 24—Multiple Inheritance 359 Part V–Sunday Morning Session 24 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 359 4689-9 ch24.f.qc 3/7/00 9:36 PM Page 360 Session Checklist ✔ Separating programs into multiple modules ✔ Using the #include directive ✔ Adding files to a Project ✔ Other preprocessor commands A ll of the programs to this point have been small enough to contain in a single .cpp source file. This is fine for the examples contained in a book such as C++ Weekend Crash Course, but this would be a severe limitation in real-world application programming. This session examines how to divide a program into parts through the clever use of project and include files. Why Divide Programs? The programmer can divide a single program into separate files sometimes known as modules. These individual source files are compiled separately and then com- bined during the build process to generate a single program. SESSION Large Programs 25 4689-9 ch25.f.qc 3/7/00 9:36 PM Page 361 The process of combining separately compiled modules into a single executable is called linking. There are a number of reasons to divide programs into more manageable pieces. First, dividing a program into modules results in a higher level of encapsulation. Classes wall off their internal members in order to provide a certain degree of safety. Programs can wall off functions to do the same thing. Remember that encapsulation was one of the advantages of object-oriented programming. Second, it is easier to comprehend and, therefore, easier to write and debug a program that consists of a number of well-thought-out modules than a single source file full of all of the classes and functions that the program uses. Next comes reuse. I used the reuse argument to help sell object-based program- ming. It is extremely difficult to keep track of a single class reused among multiple programs when a separate copy of the class is kept in each program. It is much better if a single class module is automatically shared among programs. Finally, there is the argument of time. It doesn’t take a compiler such as Visual C++ or GNU C++ very long to build the examples contained in this book using a high-speed computer like yours. Commercial programs sometimes consist of mil- lions of source lines of code. Rebuilding a program of that size can take more than 24 hours. (Almost as long as it’s taking you to get through this book!) A program- mer would not tolerate rebuilding a program like that for every single change. However, the majority of the time is spent compiling the source file into object files. The link process is much quicker. Separating Class Definition from Application Program This section begins with the EarlyBinding example from Session 22 and separates the definition of the class Student from the remainder of the application. To avoid confusion, let’s call the result SeparatedClass . Dividing the program We begin by deciding what the logical divisions of SeparatedClass should be. Clearly the application functions fn() and main() can be separated from the class Note Note Sunday Morning362 4689-9 ch25.f.qc 3/7/00 9:36 PM Page 362 definition. These functions are not reusable nor do they have anything to do with the definitions of the class Student . Similarly, the Student class does not refer to the fn() or main() functions at all. I store the application portion of the program in a file called SeparatedClass.cpp. So far the program appears as follows: // SeparatedClass - demonstrate an application separated // from the class definition #include <stdio.h> #include <iostream.h> double fn(Student& fs) { // because calcTuition() is declared virtual this // call uses the run-time type of fs to resolve // the call return fs.calcTuition(); } int main(int nArgc, char* pszArgs[]) { // the following expression calls // fn() with a Student object Student s; cout << “The value of s.calcTuition when\n” << “called virtually through fn() is “ << fn(s) << “\n\n”; // the following expression calls // fn() with a GraduateStudent object GraduateStudent gs; cout << “The value of gs.calcTuition when\n” << “called virtually through fn() is “ << fn(gs) << “\n\n”; return 0; } Session 25—Large Programs 363 Part V–Sunday Morning Session 25 4689-9 ch25.f.qc 3/7/00 9:36 PM Page 363 [...]... of the standard C++- defined include files ¼ If the constant expression after an #if directive is nonzero, then the C++ commands that follow are passed to the compiler; otherwise, they are not passed The #ifdef x directive is true if the #define constant x is defined 4 689 -9 ch26.f.qc 3/7/00 9:36 PM Page 383 Session 26 C++ Preprocessor 383 ¼ All preprocessor directives control which C++ statements the... generates the object code However, a given C++ compiler makes as many or as few passes as it needs — there is no C++ standard Even before the first compiler pass, however, the C++ preprocessor gets a chance The C++ processor scans through the cpp file looking for lines that begin with a pound (#) sign in the first column The output from the preprocessor, itself a C++ program, is fed to the compiler for... strcat() and cin> The standard C++- defined h files are included using the brackets, whereas locally defined h files are defined using the quote commands The only difference between the two is that C++ looks for files contained in quotes starting with the current directory (the directory containing the project file), whereas C++ begins the search for bracketed files in the C++ include file directories... seen so far have included the stdio.h and iostream.h file to define the functions that make up the standard C++ library This session examines the #include directive along with other preprocessor commands The C++ Preprocessor As a C++ programmer, you and I click the Build command to instruct the C++ compiler to convert our source code into an executable program We don’t typically worry about the details... column one The “ .” portion must be on the same line as the #include Tip Note The #include directive does not have the same syntax as other C++ commands This is because it is not a C++ directive at all A preprocessor makes a first pass over your C++ program before the C++ compiler executes It is this preprocessor which interprets the #include directive Dividing application code The SeparatedClass program... angle brackets in the C++ compiler directories The include file should not include any C++ functions because they will be expanded and compiled separately by the modules that include the file The contents of the include file should be limited to class definitions, global variable definitions, and other preprocessor directives 4 689 -9 ch26.f.qc 3/7/00 9:36 PM Page 375 Session 26 C++ Preprocessor 375... value of nV2 to be 4 rather than 6 and of nV1 to be 3 rather than 4 caused by the following macro expansion: 4 689 -9 ch26.f.qc 3/7/00 9:36 PM Page 3 78 3 78 Sunday Morning Because nSquareOfTwo is an int, you might expect the resulting value to be 4 rather than the actual value of 6 (2.5 * 2.5 = 6.25) C++ inline functions avoid the problems of macros: inline int square(int x) {return x * x} void fn() { int... // the following does not compile #include “myclass.h” #include “myspecialclass.h” void fn(MyClass& mc) { // might be an object of class MyClass or MySpecialClass } 4 689 -9 ch26.f.qc 3/7/00 9:36 PM Page 381 Session 26 C++ Preprocessor 381 This particular example is easily fixed; however, in a large application the relationships between the numerous include files can be bewildering Judicious use of the... “n = “ . in a single .cpp source file. This is fine for the examples contained in a book such as C++ Weekend Crash Course, but this would be a severe limitation in real-world application programming. This. Printer { public: int nVoltage; // other stuff } class Copier Sunday Morning3 58 4 689 -9 ch24.f.qc 3/7/00 9:36 PM Page 3 58 { public: int nVolatage; // other stuff } class CombinationPinterCopier. process — C++ adds student.h to SeparatedClass.cpp and compiles the result. The #include directive does not have the same syntax as other C++ commands. This is because it is not a C++ directive

Ngày đăng: 12/08/2014, 12:20

TỪ KHÓA LIÊN QUAN