Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 40 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
40
Dung lượng
504,15 KB
Nội dung
240 Structures and Classes to tell what a member function is a member of. However, the scope resolution operator :: is used with a class name, whereas the dot operator is used with objects (that is, with class variables). The scope resolution operator consists of two colons with no space between them. The class name that precedes the scope resolution operator is often called a type qualifier, because it specializes (“qualifies”) the function name to one particular type. Look at the definition of the member function DayOfYear::output given in Display 6.3. Notice that in the function definition of DayOfYear::output, we used the member names month and day by themselves without first giving the object and dot operator. That is not as strange as it may at first appear. At this point we are simply defining the member function output. This definition of output will apply to all objects of type DayOfYear, but at this point we do not know the names of the objects of type DayOfYear that we will use, so we cannot give their names. When the member function is called, as in today.output( ); all the member names in the function definition are specialized to the name of the call- ing object. So, the above function call is equivalent to the following: { switch (today.month) { case 1: . . . } cout << today.day; } type qualifier member variables in function definitions M EMBER F UNCTION D EFINITION A member function is defined similar to any other function except that the Class_Name and the scope resolution operator, ::, are given in the function heading. S YNTAX Returned_Type Class_Name :: Function_Name ( Parameter_List ) { Function_Body_Statements } E XAMPLE See Display 6.3. Note that the member variables (month and day) are not preceded by an object name and dot when they occur in a member function definition. 06_CH06.fm Page 240 Wednesday, August 13, 2003 12:54 PM Classes 241 Self-Test Exercises In the function definition for a member function, you can use the names of all mem- bers of that class (both the data members and the function members) without using the dot operator. 9. Below we have redefined the class DayOfYear from Display 6.3 so that it now has one additional member function called input. Write an appropriate definition for the member function input. class DayOfYear { public: T HE D OT O PERATOR AND THE S COPE R ESOLUTION O PERATOR Both the dot operator and the scope resolution operator are used with member names to specify of what thing they are a member. For example, suppose you have declared a class called DayOf- Year and you declare an object called today as follows: DayOfYear today; You use the dot operator to specify a member of the object today. For example, output is a member function for the class DayOfYear (defined in Display 6.3), and the following function call will output the data values stored in the object today: today.output( ); You use the scope resolution operator, ::, to specify the class name when giving the function definition for a member function. For example, the heading of the function definition for the member function output would be as follows: void DayOfYear::output( ) Remember, the scope resolution operator, ::, is used with a class name, whereas the dot operator is used with an object of that class. A C LASS I S A F ULL -F LEDGED T YPE A class is a type just like the types int and double. You can have variables of a class type, you can have parameters of a class type, a function can return a value of a class type, and more gen- erally, you can use a class type like any other type. 06_CH06.fm Page 241 Wednesday, August 13, 2003 12:54 PM 242 Structures and Classes void input( ); void output( ); int month; int day; }; 10. Given the following class definition, write an appropriate definition for the member func- tion set. class Temperature { public: void set(double newDegrees, char newScale); //Sets the member variables to the values given as //arguments. double degrees; char scale; //’F’ for Fahrenheit or ’C’ for Celsius. }; 11. Carefully distinguish between the meaning and use of the dot operator and the scope reso- lution operator, ::. ■ ENCAPSULATION A data type, such as the type int, has certain specified values, such as 0, 1, −1, 2, and so forth. You tend to think of the data type as being these values, but the operations on these values are just as important as the values. Without the operations, you could do nothing of interest with the values. The operations for the type int consist of +, −, *, /, %, and a few other operators and predefined library functions. You should not think of a data type as being simply a collection of values. A data type consists of a collection of values together with a set of basic operations defined on these values. A data type is called an abstract data type (abbreviated ADT) if the programmers who use the type do not have access to the details of how the values and operations are implemented. The predefined types, such as int, are abstract data types (ADTs). You do not know how the operations, such as + and *, are implemented for the type int. Even if you did know, you could not use this information in any C++ program. Classes, which are programmer-defined types, should also be ADTs; that is, the details of how the “opera- tions” are implemented should be hidden from, or at least irrelevant to, any program- mer who uses the class. The operations of a class are the (public) member functions of the class. A programmer who uses a class should not need to even look at the defini- tions of the member functions. The member function declarations, given in the class definition, and a few comments should be all the programmer needs in order to use the class. data types and abstract data types 06_CH06.fm Page 242 Wednesday, August 13, 2003 12:54 PM Classes 243 A programmer who uses a class also should not need to know how the data of the class is implemented. The implementation of the data should be as hidden as the imple- mentation of the member functions. In fact, it is close to impossible to distinguish between hiding the implementation of the member functions and the implementation of the data. To a programmer, the class DayOfYear (Display 6.3) has dates as data, not numbers. The programmer should not know or care whether the month March is implemented as the int value 3, the quoted string "March", or in some other way. Defining a class so that the implementation of the member functions and the imple- mentation of the data in objects are not known, or is at least irrelevant, to the program- mer who uses the class is known by a number of different terms. The most common terms used are information hiding, data abstraction, and encapsulation, each of which means that the details of the implementation of a class are hidden from the pro- grammer who uses the class. This principle is one of the main tenets of object-oriented programming (OOP). When discussing OOP, the term that is used most frequently is encapsulation. One of the ways to apply this principle of encapsulation to your class definitions is to make all member variables private, which is what we discuss in the next subsection. ■ PUBLIC AND PRIVATE MEMBERS Look back at the definition of the type DayOfYear given in Display 6.3. In order to use that class, you need to know that there are two member variables of type int that are named month and day. This violates the principle of encapsulation (information hiding) that we discussed in the previous subsection. Display 6.4 is a rewritten version of the class DayOfYear that better conforms to this encapsulation principle. Notice the words private: and public: in Display 6.4. All the items that follow the word private: (in this case the member variables month and day) are said to be private, which means that they cannot be referenced by name anyplace except within the definitions of the member functions of the class DayOfYear. For example, with this changed definition of the class DayOfYear, the following two assignments and other indicated code are no longer permitted in the main function of the program and are not permitted in any other function definition, except for member functions of the class DayOfYear: DayOfYear today; //This line is OK. today.month = 12;//ILLEGAL today.day = 25;//ILLEGAL cout << today.month;//ILLEGAL cout << today.day;//ILLEGAL if (today.month == 1) //ILLEGAL cout << "January"; Once you make a member variable a private member variable, there is no way to change its value (or to reference the member variable in any other way) except by using one of the member functions. That means that the compiler will enforce the hiding of encapsu- lation private: private member variable 06_CH06.fm Page 243 Wednesday, August 13, 2003 12:54 PM 244 Structures and Classes Display 6.4 Class with Private Members (part 1 of 3) 1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 class DayOfYear 5 { 6 public: 7 void input( ); 8 void output( ); 9 void set(int newMonth, int newDay); 10 //Precondition: newMonth and newDay form a possible date. 11 void set(int newMonth); 12 //Precondition: 1 <= newMonth <= 12 13 //Postcondition: The date is set to the first day of the given month. 14 int getMonthNumber( ); //Returns 1 for January, 2 for February, etc. 15 int getDay( ); 16 private: 17 int month; 18 int day; 19 }; 20 int main( ) 21 { 22 DayOfYear today, bachBirthday; 23 cout << "Enter today’s date:\n"; 24 today.input( ); 25 cout << "Today’s date is "; 26 today.output( ); 27 cout << endl; 28 bachBirthday.set(3, 21); 29 cout << "J. S. Bach’s birthday is "; 30 bachBirthday.output( ); 31 cout << endl; 32 if ( today.getMonthNumber( ) == bachBirthday.getMonthNumber( ) && 33 today.getDay( ) == bachBirthday.getDay( ) ) 34 cout << "Happy Birthday Johann Sebastian!\n"; 35 else 36 cout << "Happy Unbirthday Johann Sebastian!\n"; 37 38 return 0; 39 } 40 //Uses iostream and cstdlib: 41 void DayOfYear::set(int newMonth, int newDay) Private members This is an improved version of the class DayOfYear that we gave in Display 6.3. Note that the function name set is overloaded. You can overload a member function just like you can overload any other function. 06_CH06.fm Page 244 Wednesday, August 13, 2003 12:54 PM Classes 245 Display 6.4 Class with Private Members (part 2 of 3) 42 { 43 if ((newMonth >= 1) && (newMonth <= 12)) 44 month = newMonth; 45 else 46 { 47 cout << "Illegal month value! Program aborted.\n"; 48 exit(1); 49 } 50 if ((newDay >= 1) && (newDay <= 31)) 51 day = newDay; 52 else 53 { 54 cout << "Illegal day value! Program aborted.\n"; 55 exit(1); 56 } 57 } 58 //Uses iostream and cstdlib: 59 void DayOfYear::set(int newMonth) 60 { 61 if ((newMonth >= 1) && (newMonth <= 12)) 62 month = newMonth; 63 else 64 { 65 cout << "Illegal month value! Program aborted.\n"; 66 exit(1); 67 } 68 day = 1; 69 } 70 71 int DayOfYear::getMonthNumber( ) 72 { 73 return month; 74 } 75 int DayOfYear::getDay( ) 76 { 77 return day; 78 } 79 //Uses iostream and cstdlib: 80 void DayOfYear::input( ) 81 { 82 cout << "Enter the month as a number: "; 83 cin >> month; 84 cout << "Enter the day of the month: "; 85 cin >> day; Mutator functions Accessor functions Private members may be used in member function definitions (but not elsewhere). 06_CH06.fm Page 245 Wednesday, August 13, 2003 12:54 PM 246 Structures and Classes the implementation of the data for the class DayOfYear. If you look carefully at the pro- gram in Display 6.4, you will see that the only place the member variable names month and day are used is in the definitions of the member functions. There is no reference to today.month, today.day, bachBirthday.month, or bachBirthday.day anyplace outside the definitions of member functions. All the items that follow the word public: (in this case the member functions) are said to be public, which means that they can be referenced by name anyplace. There are no restrictions on the use of public members. Any member variables can be either public or private. Any member functions can be public or private. However, normal good programming practices require that all mem- ber variables be private and that typically most member functions be public. You can have any number of occurrences of public and private access specifiers in a class definition. Every time you insert the label public: the list of members changes from private to public. Every time you insert the label private: the list of members changes back to being private members. You need not have just one public and one private group of members. However, it is common to have just one public section and one private section. Display 6.4 Class with Private Members (part 3 of 3) 86 if ((month < 1) || (month > 12) || (day < 1) || (day > 31)) 87 { 88 cout << "Illegal date! Program aborted.\n"; 89 exit(1); 90 } 91 } 92 void DayOfYear::output( ) 93 < The rest of the definition of DayOfYear::output is given in Display 6.3. > S AMPLE D IALOGUE Enter today’s date: Enter the month as a number: 3 Enter the day of the month: 21 Today’s date is March 21 J. S. Bach’s birthday is March 21 Happy Birthday Johann Sebastian! public: public member variable 06_CH06.fm Page 246 Wednesday, August 13, 2003 12:54 PM Classes 247 There is no universal agreement about whether the public members should be listed first or the private members should be listed first. The majority seem to prefer listing the public members first. This allows for easy viewing of the portions programmers using the class actually get to use. You can make your own decision on what you wish to place first, but the examples in the book will go along with the majority and list the public members before the private members. In one sense C++ seems to favor placing the private members first. If the first group of members has neither the public: nor the private: specifier, then members of that group will automatically be private. You will see this default behavior used in code and should be familiar with it. However, we will not use it in this book. ■ ACCESSOR AND MUTATOR FUNCTIONS You should always make all member variables in a class private. You may sometimes need to do something with the data in a class object, however. The member functions will allow you to do many things with the data in an object, but sooner or later you will want or need to do something with the data for which there is no member function. How can you do anything new with the data in an object? The answer is that you can do anything you might reasonably want, provided you equip your classes with suitable accessor and mutator functions. These are member functions that allow you to access and change the data in an object in a very general way. Accessor functions allow you to read the data. In Display 6.4, the member functions getMonthNumber and getDay are accessor functions. The accessor functions need not literally return the values of each member variable, but they must return something equivalent to those values. For exam- ple, for a class like DayOfYear, you might have an accessor function return the name of the month as some sort of string value, rather than return the month as a number. Mutator functions allow you to change the data. In Display 6.4, the two functions named set are mutator functions. It is traditional to use names that include the word get for accessor functions and names that include the word set for mutator functions. (The functions input and output in Display 6.4 are really mutator and accessor func- tions, respectively, but I/O is such a special case that they are usually just called I/O functions rather than accessor or mutator functions.) Your class definitions should always provide an adequate collection of accessor and mutator functions. It may seem that accessor and mutator functions defeat the purpose of making mem- ber variables private, but that is not so. Notice the mutator function set in Display 6.4. It will not allow you to set the month member variable to 13 or to any number that does not represent a month. Similarly, it will not allow you to set the day member variable to any number that is not in the range 1 to 31 (inclusive). If the variables were public you could set the data to values that do not make sense for a date. (As it is, you can still set the data to values that do not represent a real date, such as February 31, but it would be easy to exclude these dates as well. We did not exclude these dates to keep the example simple.) With mutator functions, you can control and filter changes to the data. accessor function mutator function 06_CH06.fm Page 247 Wednesday, August 13, 2003 12:54 PM 248 Structures and Classes Tip Self-Test Exercises 12. Suppose your program contains the following class definition, class Automobile { public: void setPrice(double newPrice); void setProfit(double newProfit); double getPrice( ); private: double price; double profit; double getProfit( ); }; and suppose the main function of your program contains the following declaration and that the program somehow sets the values of all the member variables to some values: Automobile hyundai, jaguar; Which of the following statements are then allowed in the main function of your program? hyundai.price = 4999.99; jaguar.setPrice(30000.97); double aPrice, aProfit; aPrice = jaguar.getPrice( ); aProfit = jaguar.getProfit( ); aProfit = hyundai.getProfit( ); hyundai = jaguar; 13. Suppose you change Self-Test Exercise 12 so that in the definition of the class Automobile all member variables are public instead of private. How would this change your answer to the question in Self-Test Exercise 12? 14. Explain what public: and private: mean in a class definition. 15. a. How many public: sections are required in a class for the class to be useful? b. How many private: sections are required in a class? S EPARATE I NTERFACE AND I MPLEMENTATION The principle of encapsulation says that you should define classes so that a programmer who uses the class need not be concerned with the details of how the class is implemented. The programmer who uses the class need only know the rules for how to use the class. The rules for how to use the class are known as the ii ii nn nn tt tt ee ee rr rr ff ff aa aa cc cc ee ee or AA AA PP PP II II . There is some disagreement on exactly what the initials API stand for, but it is generally agreed that they stand for something like application programmer interface API 06_CH06.fm Page 248 Wednesday, August 13, 2003 12:54 PM Classes 249 Tip interface or abstract programming interface or something similar. In this book we will call these rules the interface for the class. It is important to keep in mind a clear distinction between the interface and the implementation of a class. If your class is well designed, then any programmer who uses the class need only know the interface for the class and need not know any details of the implementation of the class. A class whose interface and implementation are separated in this way is sometimes called an abstract data type (ADT) or a nicely encapsulated class. In Chapter 11 we will show you how to separate the interface and implementation by placing them in different files, but the important thing is to keep them conceptually separated. For a C++ class, the ii ii nn nn tt tt ee ee rr rr ff ff aa aa cc cc ee ee consists of two sorts of things: the comments, usually at the begin- ning of the class definition, that tell what the data of the object is supposed to represent, such as a date or bank account or state of a simulated car wash; and the public member functions of the class along with the comments that tell how to use these public member functions. In a well- designed class, the interface of the class should be all you need to know in order to use the class in your program. The ii ii mm mm pp pp ll ll ee ee mm mm ee ee nn nn tt tt aa aa tt tt ii ii oo oo nn nn of a class tells how the class interface is realized as C++ code. The implemen- tation consists of the private members of the class and the definitions of both the public and pri- vate member functions. Although you need the implementation in order to run a program that uses the class, you should not need to know anything about the implementation in order to write the rest of a program that uses the class; that is, you should not need to know anything about the implementation in order to write the main function of the program and to write any nonmember functions or other classes used by the main function. The most obvious benefit you derive from cleanly separating the interface and implementation of your classes is that you can change the implementation without having to change the other parts of your program. On large programming projects this division between interface and implemen- tation will facilitate dividing the work among different programmers. If you have a well-designed interface, then one programmer can write the implementation for the class while other program- mers write the code that uses the class. Even if you are the only programmer working on a project, you have divided one larger task into two smaller tasks, which makes your program easier to design and to debug. A T EST FOR E NCAPSULATION If your class definition produces an ADT (that is, if it properly separates the interface and the implementation), then you can change the implementation of the class (that is, change the data representation and/or change the implementation of some member functions) without needing to change any (other) code for any program that uses the class definition. This is a sure test for whether you have defined an ADT or just some class that is not properly encapsulated. For example, you can change the implementation of the class DayOfYear in Display 6.4 to the following and no program that uses this class definition would need any changes: class DayOfYear { implemen- tation 06_CH06.fm Page 249 Wednesday, August 13, 2003 12:54 PM [...]... ignore classes completely or relegate them to a minor role, but then you are really programming in C, not C++ Self-Test Exercises 16 When you define a C++ class, should you make the member variables public or private? Should you make the member functions public or private? 17 When you define a C++ class, what items are considered part of the interface? What items are considered part of the implementation?... difficult s STRUCTURES VERSUS CLASSES Structures are normally used with all member variables public and with no member functions However, in C++ a structure can have private member variables and both public and private member functions Aside from some notational differences, a C++ structure can do anything a class can do Having said this and satisfied the “truth in advertising” requirement, we advocate that... a class or structure type A function can return values of a class or structure type A member function for a class can be overloaded in the same way as ordinary functions are overloaded When defining a C++ class, you should separate the interface and implementation so that any programmer who uses the class need only know the interface and need not even look at the implementation This is the principle... member function, the scope resolution operator is used to tell the compiler that this function is the one declared in the class 12 hyundai.price = 4999.99; //ILLEGAL price is private jaguar.setPrice(30000. 97); //LEGAL double aPrice, aProfit;//LEGAL aPrice = jaguar.getPrice( );//LEGAL aProfit = jaguar.getProfit( );//ILLEGAL getProfit is //private aProfit = hyundai.getProfit( );//ILLEGAL getProfit is // private... outputs the count Embed your class definition in a test program 3 The type Point is a fairly simple data type, but under another name (the template class pair) this data type is defined and used in the C++ Standard Template Library, although you need not know anything about the Standard template Library to do this exercise Write a definition of a class named Point that might be used to store and manipulate... you declare the object As we will see later in this book, there are other initializing actions you might also want to take, but initializing member variables is the most common sort of initialization C++ includes special provisions for such initializations When you define a class you can define a special kind of member function known as a constructor A constructor is a member function that is automatically... arguments 5 and 5 The 07_CH07.fm Page 260 Wednesday, August 13, 2003 12:58 PM 260 Constructors and Other Tools result is conceptually equivalent to the following (although you cannot write it this way in C++) : DayOfYear date1, date2; //PROBLEMS BUT FIXABLE date1.DayOfYear(7, 4); //VERY ILLEGAL date2.DayOfYear(5, 5); //VERY ILLEGAL As the comments indicate, you cannot place the above three lines in your... a default constructor This name can be misleading because sometimes it is generated by default (that is, automatically) and sometimes it is not Here is the full story If you define a class and include absolutely no constructors of any kind, then a default constructor will be automatically created This default constructor does not do anything, but it does give you an uninitialized object of the class... ); private: int data1; double data2; }; You should recognize the following as a legal way to declare an object of type SampleClass and call the constructor for that class: SampleClass myVariable(7, 7. 77); However, the following is illegal: SampleClass yourVariable; The compiler interprets the above declaration as including a call to a constructor with no arguments, but there is no definition for a constructor . in C, not C++. 16. When you define a C++ class, should you make the member variables public or private? Should you make the member functions public or private? 17. When you define a C++ class,. member functions. However, in C++ a structure can have private member variables and both public and private member functions. Aside from some notational differences, a C++ structure can do anything. then allowed in the main function of your program? hyundai.price = 4999.99; jaguar.setPrice(30000. 97); double aPrice, aProfit; aPrice = jaguar.getPrice( ); aProfit = jaguar.getProfit( ); aProfit