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

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

10 263 0

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

THÔNG TIN TÀI LIỆU

Nội dung

Pointers and Virtual Functions 647 (partially filled array of doubles) and its derived class PFArrayDBak (partially filled array of doubles with backup). We discussed these classes in Chapter 14. That was before we knew about virtual functions, and so the destructor in the base class PFArrayD was not marked virtual. In Display 15.8 we have summarized all the facts we need about the classes PFArrayD and PFArrayDBak so that you need not look back to Chapter 14. Consider the following code: PFArrayD *p = new PFArrayDBak; . . . delete p; Since the destructor in the base class is not marked virtual, only the destructor for the base class ( PFArrayD) will be invoked. This will return the memory for the member array a (declared in PFArrayD) to the freestore, but the memory for the member array b (declared in PFArrayD- Bak ) will never be returned to the freestore (until the program ends). On the other hand, if (unlike Display 15.8) the destructor for the base class PFArrayD were marked virtual, then when delete is applied to p, the constructor for the class PFArrayDBak would be invoked (since the object pointed to is of type PFArrayDBak). The destructor for the class PFArrayDBak would delete the array b and then automatically invoke the constructor for the base class PFArrayD, and that would delete the member array a. So, with the base class destructor marked as virtual, all the memory is returned to the freestore. To prepare for eventual- ities such as these, it is best to always mark destructors as virtual. ■ DOWNCASTING AND UPCASTING You might think some sort of type casting would allow you to easily get around the slic- ing problem. However, things are not that simple. The following is illegal: Pet vpet; Dog vdog; //Dog is a derived class with base class Pet. . . . vdog = static_cast<Dog>(vpet); //ILLEGAL! However, casting in the other direction is perfectly legal and does not even need a cast- ing operator: vpet = vdog; //Legal (but does produce the slicing problem.) Casting from a descendant type to an ancestor type is known as upcasting, since you are moving up the class hierarchy. Upcasting is safe because you are simply disre- garding some information (disregarding member variables and functions). So, the fol- lowing is perfectly safe: vpet = vdog; upcasting 648 Polymorphism and Virtual Functions Display 15.8 Review of the Classes PFArrayD and PFArrayDBak class PFArrayD { public: PFArrayD( ); . . . ~PFArrayD( ); protected: double *a; //for an array of doubles. int capacity; //for the size of the array. int used; //for the number of array positions currently in use. }; PFArrayD::PFArrayD( ) : capacity(50), used(0) { a = new double[capacity]; } PFArrayD::~PFArrayD( ) { delete [] a; } class PFArrayDBak : public PFArrayD { public: PFArrayDBak( ); . . . ~PFArrayDBak( ); private: double *b; //for a backup of main array. int usedB; //backup for inherited member variable used. }; PFArrayDBak::PFArrayDBak( ) : PFArrayD( ), usedB(0) { b = new double[capacity]; } PFArrayDBak::~PFArrayDBak( ) { delete [] b; } The destructors should be virtual, but we had not yet covered virtual functions when we wrote these classes. Some details about the derived class PFArrayDBak. A complete definition of PFArrayDBak is given in Displays 14.10 and 14.11, but this display has all the details you need for this chapter. Some details about the base class PFArrayD. A more complete definition of PFArrayD is given in Displays 14.8 and 14.9, but this display has all the details you need for this chapter. Pointers and Virtual Functions 649 Casting from an ancestor type to a descended type is called downcasting and is very dangerous, since you are assuming that information is being added (added member variables and functions). The dynamic_cast that we discussed briefly in Chapter 1 is used for downcasting. It is of some possible use in defeating the slicing problem but is dangerously unreliable and fraught with pitfalls. A dynamic_cast may allow you to downcast, but it only works for pointer types, as in the following: Pet *ppet; ppet = new Dog; Dog *pdog = dynamic_cast<Dog*>(ppet); //Dangerous! We have had downcasting fail even in situations as simple as this, and so we do not rec- ommend it. The dynamic_cast is supposed to inform you if it fails. If the cast fails, the dynamic_cast should return NULL (which is really the integer 0). 2 If you want to try downcasting keep the following points in mind: 1. You need to keep track of things so that you know the information to be added is indeed present. 2. Your member functions must be virtual, since dynamic_cast uses the virtual func- tion information to perform the cast. ■ HOW C++ IMPLEMENTS VIRTUAL FUNCTIONS You need not know how a compiler works in order to use it. That is the principle of information hiding, which is basic to all good program design philosophies. In particu- lar, you need not know how virtual functions are implemented in order to use virtual functions. However, many people find that a concrete model of the implementation helps their understanding, and when reading about virtual functions in other books you are likely to encounter references to the implementation of virtual functions. So, we will give a brief outline of how they are implemented. All compilers for all languages (including C++) that have virtual functions typically implement them in basically the same way. If a class has one or more member functions that are virtual, the compiler creates what is called a virtual function table for that class. This table has a pointer (memory address) for each virtual member function. The pointer points to the location of the correct code for that member function. If one virtual function was inherited and not changed, then its table entry points to the definition for that function that was given in the parent class (or other ancestor class if need be). If another virtual function had a new definition in the class, then the pointer in the table for that member function points to that definition. (Remember that the property of being a virtual function is 2 The standard says “The value of a failed cast to pointer type is the null pointer of the required result type. A failed cast to a reference type throws a bad_cast.” downcasting virtual function table 650 Polymorphism and Virtual Functions Self-Test Exercises inherited, so once a class has a virtual function table, then all its descendant classes have a virtual function table.) Whenever an object of a class with one or more virtual functions is created, another pointer is added to the description of the object that is stored in memory. This pointer points to the class’s virtual function table. When you make a call to a member function using a pointer (yep, another one) to the object, the runtime system uses the virtual function table to decide which definition of a member function to use; it does not use the type of the pointer. Of course, this all happens automatically, so you need not worry about it. A com- piler writer is even free to implement virtual functions in some other way as long as it works correctly (although it never actually is implemented in a different way). 8. Why is the following illegal? Pet vpet; Dog vdog; //Dog is a derived class with base class Pet. . . . vdog = static_cast<Dog>(vpet); //ILLEGAL! ■ Late binding means that the decision of which version of a member function is appropriate is decided at runtime. In C++, member functions that use late binding are called virtual functions. Polymorphism is another word for late binding. ■ A pure virtual function is a member function that has no definition. A pure virtual function is indicated by the word virtual and the notation = 0 in the member function declaration. A class with one or more pure virtual functions is called an abstract class. ■ An abstract class is a type and can be used as a base class to derive other classes. However, you cannot create an object of an abstract class type (unless it is an object of some derived class). ■ You can assign an object of a derived class to a variable of its base class (or any ances- tor class), but the member variables that are not in the base class are lost. This is known as the slicing problem. ■ If the domain type of the pointer pAncestor is a base class for the domain type of the pointer pDescendant, then the following assignment of pointers is allowed: pAncestor = pDescendant; Chapter Summary Programming Projects 651 Moreover, none of the data members or member functions of the dynamic variable being pointed to by pDescendant will be lost. Although all the extra fields of the dynamic variable are there, you will need virtual member functions to access them. ■ It is a good programming practice to make destructors virtual. ANSWERS TO SELF-TEST EXERCISES 1. In essence there is no difference among the three terms. They all refer to the same topic. There is only a slight difference in their usage. (Virtual function is a kind of member function, late binding refers to the mechanism used to decide which function definition to use when a func- tion is virtual, and polymorphism is another name for late binding.) 2. The output would change to the following: Discounted item is not cheaper. 3. Yes, it is legal to have an abstract class in which all member functions are pure virtual func- tions. 4. a. Illegal, because Employee is an abstract class. b. Legal. c. Legal, because an abstract class is a type. 5. There would be no members to assign to the derived class’s added members. 6. Although it is legal to assign a derived class object to a base class variable, this discards the parts of the derived class object that are not members of the base class. This situation is known as the slicing problem. 7. If the base class function carries the virtual modifier, then the derived class member function is called. If the base class member function does not have the virtual modifier, then the base class member function is called. 8. Since Dog can have more member variables than Pet, the object vpet may not have enough data for all the member variables of type Dog. PROGRAMMING PROJECTS 1. Consider a graphics system that has classes for various figures, say rectangles, squares, trian- gles, circles, and so on. For example, a rectangle might have data members height, width, and center point, while a square and circle might have only a center point and an edge length or radius, respectively. In a well-designed system these would be derived from a common class, Figure. You are to implement such a system. The class Figure is the base class. You should add only Rectangle and Triangle classes derived from Figure. Each class has stubs for member functions erase and draw. Each of these member functions outputs a message telling what function has been called and what the class of the calling object is. Since these are just stubs, they do nothing more than 652 Polymorphism and Virtual Functions output this message. The member function center calls erase and draw to erase and redraw the figure at the center. Because you have only stubs for erase and draw, center will not do any “centering” but will call the member functions erase and draw. Also, add an output message in the member function center that announces that center is being called. The member functions should take no arguments. There are three parts to this project: a. Do the class definitions using no virtual functions. Compile and test. b. Make the base class member functions virtual. Compile and test. c. Explain the difference in results. For a real example, you would have to replace the definition of each of these member func- tions with code to do the actual drawing. You will be asked to do this in Programming Project 2. Use the following main function for all testing: //This program tests Programming Problem 1. #include <iostream> #include "figure.h" #include "rectangle.h" #include "triangle.h" using std::cout; int main( ) { Triangle tri; tri.draw( ); cout << "\nDerived class Triangle object calling center( ).\n"; tri.center( ); //Calls draw and center Rectangle rect; rect.draw( ); cout << "\nDerived class Rectangle object calling center().\n"; rect.center( ); //Calls draw and center return 0; } 2. Flesh out Programming Problem 1. Give new definitions for the various constructors and member functions Figure::center, Figure::draw, Figure::erase, Triangle::draw, Triangle::erase, Rectangle::draw and Rectangle::erase so that the draw func- tions actually draw figures on the screen by placing the character ’*’ at suitable locations on the screen. For the erase functions, you can simply clear the screen (by outputting blank lines or by doing something more sophisticated). There are a lot of details in this and you will have to decide on some of them on your own. For additional online Programming Projects, click the CodeMate icons below. 1.7 16 Templates 16.1 FUNCTION TEMPLATES 654 Syntax for Function Templates 656 Pitfall: Compiler Complications 659 Example: A Generic Sorting Function 661 Tip: How to Define Templates 665 Pitfall: Using a Template with an Inappropriate Type 665 16.2 CLASS TEMPLATES 667 Syntax for Class Templates 667 Example: An Array Template Class 671 The vector and basic_string Templates 677 16.3 TEMPLATES AND INHERITANCE 678 Example: Template Class for a Partially Filled Array with Backup 678 CHAPTER SUMMARY 684 ANSWERS TO SELF-TEST EXERCISES 684 PROGRAMMING PROJECTS 688 16_CH16.fm Page 653 Monday, August 18, 2003 1:04 PM 16 Templates All men are mortal. Aristotle is a man. Therefore, Aristotle is mortal. All X’s are Y. Z is an X. Therefore, Z is Y. All cats are mischievous. Garfield is a cat. Therefore, Garfield is mischievous. A Short Lesson on Syllogisms INTRODUCTION This chapter discusses C++ templates, which allow you to define functions and classes that have parameters for type names. This enables you to design func- tions that can be used with arguments of different types and to define classes that are much more general than those you have seen before this chapter. Section 16.1 requires only material from Chapters 1 through 5. Section 16.2 uses material from Section 16.1 as well as Chapters 1 through 11 but does not require the material from Chapter 12 through 15. Section 16.3 requires the previous sections as well as Chapter 14 on inheritance and all the chapters needed for Section 16.2. Section 16.3 does mark some member func- tions as virtual . Virtual functions are covered in Chapter 15. However, this use of virtual functions is not essential to the material presented. It is possible to read Section 16.3 ignoring (or even omitting) all occurrences of the key- word virtual . Function Templates Many of our previously discussed C++ function definitions have an underlying algorithm that is much more general than the algorithm we gave in the func- tion definition. For example, consider the function swapValues , which we first discussed in Chapter 4. For reference, we now repeat the function definition: void swapValues(int& variable1, int& variable2) { int temp; 16.1 16_CH16.fm Page 654 Monday, August 18, 2003 1:04 PM Function Templates 655 temp = variable1; variable1 = variable2; variable2 = temp; } Notice that the function swapValues applies only to variables of type int . Yet the algorithm given in the function body could just as well be used to swap the values in two variables of type char . If we want to also use the function swapValues with vari- ables of type char , we can overload the function name swapValues by adding the fol- lowing definition: void swapValues(char& variable1, char& variable2) { char temp; temp = variable1; variable1 = variable2; variable2 = temp; } But there is something inefficient and unsatisfying about these two definitions of the swapValues function: They are almost identical. The only difference is that one defini- tion uses the type int in three places and the other uses the type char in the same three places. Proceeding in this way, if we wanted to have the function swapValues apply to pairs of variables of type double , we would have to write a third almost identical func- tion definition. If we wanted to apply swapValues to still more types, the number of almost identical function definitions would be even larger. This would require a good deal of typing and would clutter up our code with lots of definitions that look identi- cal. We should be able to say that the following function definition applies to variables of any type: void swapValues( Type_Of_The_Variables & variable1, Type_Of_The_Variables & variable2) { Type_Of_The_Variables temp; temp = variable1; variable1 = variable2; variable2 = temp; } As we will see, something like this is possible. We can define one function that applies to all types of variables, although the syntax is a bit more complicated than what we have shown above. The proper syntax is described in the next subsection. 16_CH16.fm Page 655 Monday, August 18, 2003 1:04 PM 656 Templates ■ SYNTAX FOR FUNCTION TEMPLATES Display 16.1 shows a C++ template for the function swapValues . This function tem- plate allows you to swap the values of any two variables, of any type, as long as the two variables have the same type. The definition and the function declaration begin with the line template<class T> This is often called the template prefix , and it tells the compiler that the definition or function declaration that follows is a template and that T is a type parameter . In this context the word class actually means type. 1 As we will see, the type parameter T can be replaced by any type, whether the type is a class or not. Within the body of the func- tion definition the type parameter T is used just like any other type. The function template definition is, in effect, a large collection of function defini- tions. For the function template for swapValues shown in Display 16.1, there is, in effect, one function definition for each possible type name. Each of these definitions is obtained by replacing the type parameter T with a type name. For example, the func- tion definition shown below is obtained by replacing T with the type name double : void swapValues(double& variable1, double& variable2) { double temp; temp = variable1; variable1 = variable2; variable2 = temp; } Another definition for swapValues is obtained by replacing the type parameter T in the function template with the type name int . Yet another definition is obtained by replacing the type parameter T with char . The one function template shown in Display 16.1 overloads the function name swapValues so that there is a slightly different func- tion definition for every possible type. The compiler will not literally produce definitions for every possible type for the function name swapValues , but it will behave exactly as if it had produced all those function definitions. A separate definition will be produced for each different type for which you use the template, but not for any types you do not use. Only one definition is generated for a single type regardless of the number of times you use the template for that type. Notice that the function swapValues is called twice in Display 16.1: One 1 In fact, the ANSI/ISO standard provides that the keyword typename may be used instead of class in the template prefix. It would make more sense to use the keyword typename rather than class , but everybody uses class , so we will do the same. (It is often true that consistency in coding is more important than optimality.) template prefix type parameter A template overloads the function name A template overloads the function name 16_CH16.fm Page 656 Monday, August 18, 2003 1:04 PM . Templates 16.1 FUNCTION TEMPLATES 654 Syntax for Function Templates 656 Pitfall: Compiler Complications 659 Example: A Generic Sorting Function 661 Tip: How to Define Templates 665 Pitfall: Using a Template. in the next subsection. 16_CH16.fm Page 655 Monday, August 18, 2003 1:04 PM 656 Templates ■ SYNTAX FOR FUNCTION TEMPLATES Display 16.1 shows a C++ template for the function swapValues . 678 Example: Template Class for a Partially Filled Array with Backup 678 CHAPTER SUMMARY 684 ANSWERS TO SELF-TEST EXERCISES 684 PROGRAMMING PROJECTS 688 16_CH16.fm Page 653 Monday, August 18, 2003

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

TỪ KHÓA LIÊN QUAN