![]() |
![]() |
![]() |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
![]() |
![]() |
To access the contents, click the chapter and section titles.
C++ in Plain English
In addition to these functions, a complete implementation would also include the operator and conversion functions for the CStr class. Now compare the version of CIoStr youve just seen to the earlier one: #include cstr.h> class CIoStr : public CStr { public: void input(void); void output(void); }; The effect is the same with either approach: the CIoStr class supports all the same member functions that CStr does and then adds two new functions (input and output). Clearly, the version that uses inheritance has a much simpler, cleaner, and more elegant syntax. In theory, you can think of inheritance and object containment declaring one object inside anotheras being different. Inheritance, according to object-oriented theory, should be used in situations of the form A is a kind of B. A, the derived class, is simply a more specialized version of B, the base class. Adopting this approach, we would say that CIoStr is a more specialized kind of string than CStr, and therefore it adds new functions. But both are strings. With object containment, on the other hand, one class usesor is a client ofthe other class. For example, you might create a CAddress class that contains several strings (CStr objects), among other data. class CAddress { CStr name; CStr line1; CStr line2; int months_at_residence; }; You wouldnt consider CAddress to be simply a more specialized type of string; it is a fundamentally different type from CStr. These cases are fairly clear-cut. But in some cases it can be more difficult to decide whether one class should be derived from another (inheritance) or whether one class should contain the other. The best policy is to remember that inheritance is a convenient bit of syntax for including all the members of one class in another and is therefore not very different from containment. (Inheritance also has a critical role in virtual functions, a fact well look at in the next chapter.) Another way to simulate inheritance, by the way, is to use object containment but rely on the user of the object to adjust function calls. #include cstr.h class CIoStr : public CStr { public: CStr str; void input(void); void output(void); }; Its easy to write this class, but its not as convenient to use. Most functions have to be called through the data member str. For example, the following code fragment calls the cpy function: CIoStr Iostring; . . . iostring.str.cpy(Lets go to the Oscars.); If you had used inheritance, the call to cpy would be made more directly. The moral of the story is that in situations such as this one, inheritance can often be more convenient than other techniques. Public, Private, and Protected AccessSo far I have introduced one member-access keyword: public. In certain respects, this keyword is the most useful. But C++ has three member-access keywords: public, private, and protected. Within class declarations, private is the default member access level. Because it is the default, you can usually get away with not using it. However, making private access explicit is a good idea because it makes the class declarations easier to read. For example, here is the CFontIoStr declaration with the private keyword used to clarify the access level of ptsize: #include CIoStr.h class CFontIoStr : public CIoStr { private: int ptsize; public: void clr(void) {cpy( ); } void setchar(int c, int n); void set_font(int size); }; The third access level, protected, is an intermediate level between private and public. A member with protected access can be accessed with the scope of the class and its derived classes but is private in any other scope. Look at the CIoStr declaration again. This class inherits from CStr, the original class, and adds two new functions. #include cstr.h class CIoStr : public CStr { public: void input(void); void output(void); }; In the declaration of CStr, the two data members (pData and nLength) were declared private. This was done by default, because they were not declared with the public keyword. This is fine unless you want the new functions in CIoStr to be able to refer to the data members. At that point, there is a problem: void CIoStr::print(void) { printf(%s, pData); //ERROR! No access to pData } Earlier, I said that a derived class inherits all the members of its base class. This is true, even here. The data member pData is present in every instance of CIoStr and can be accessed indirectly through a CStr member function such as get or cpy. However, pData is not visible in function definitions for the derived class CIoStr. If pData and nLength had been declared with the protected keyword in the CStr declaration, then they could be accessed in CStr function definitions and in CIoStr function definitions. class CStr { protected: char *pData; int nLength; public: Cstr(); //Constructors CStr(char *s); CStr(const CStr &str); ~CStr(); // Destructor . . . Figure 8.3 summarizes the three levels of member-access rightspublic, protected, and private.
Is it better to declare data members as protected or as private? The answer depends on the purpose of the members. In the case of the CStr class, it probably isnt necessary to declare pData and nLength as protected, because a series of public functionsget, getlength, cpy, and catprovide complete access to the data.
|
![]() |
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement. |