![]() |
![]() |
![]() |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
![]() |
![]() |
To access the contents, click the chapter and section titles.
C++ in Plain English
The definitions of the new CIoStr functions are simple. #include <stdio.h> #include ciostr.h void CIoStr::output(void) printf(%s, get()); } void CIoStr::input(void) { char buffer[256]; gets(buffer); cpy((buffer); } Only two functions (input and output) are defined here; these functions are defined with the CIoStr:: prefix. Many other functions are inherited from the base class CStr, all of them were defined with the CStr:: prefix. For example: void CStr::cpy(char *s) { // Copy string arg. int n; n = strlen(s); if (nLength <> n) { if (pData) free(pData); pData = (char*) malloc(n + 1); nLength = n; } strcpy(pData, s); } So CIoStr supports two kinds of functions: those contributed by the base class, CStr, and those added by CIoStr itself. The prefix varies depending on where the member function is declared and definedin the base class or in the derived class. Overriding Functions and Clarifying ScopeYouve already seen inheritance at work, whether or not youve noticed it. The definitions of the CIoStr functions use functions from the base class. Take another look at the input function: void CIoStr::input(void) { char buffer[256]; gets(buffer); cpy(buffer); } The last statement in the definition calls the CStr::cpy function. As in other member-function definitions, the function can be referred to as cpy, because it is a member of CIoStr (by virtue of inheritance from CStr). The situation gets more complex, though, if the derived class, CIoStr, overrides one or more functions in the base class. In this example, there is little motivation to override any of the CStr functions, but it is still perfectly valid. For example: class CIoStr : public CStr { public: void input(void); void output(void); // Overridden from base class CStr void cpy(char *s); }; void CIoStr::cpy(char *s) { // Alternative implementation of cpy function } Now there are two versions of cpy: one defined in the CIoStr class and a second one defined in its base class, CStr. Member functions of CIoStr can call either of these functions. But now, to call the original version of cpy, you must use the scope operator (::) to specify that the CStr version should be called. Otherwise, the new version (CIoStr::cpy) is assumed. void CIoStr::input(void) { char buffer[256]; gets(buffer); CStr::cpy(buffer); // Call base-class version // of cpy. } In general, when the compiler sees a name, it attempts to resolve the scope in the following order:
Inheritance HierarchiesAs the last section hinted, base classes can have their own base classes, and derived classes can have derived classes. This arrangement may sound complex, so lets take a simple example. Suppose you want to declare a class that has even more functions available than CIoStr has. You can derive a class from CIoStr even though CIoStr itself was derived from CStr. #include ciostr.h class CFontIoStr : public CIoStr { int ptsize; public: void clr(void) { cpy( ); } void setchar(int c, int n); void set-font(int size); }; This new class, CFontIoStr, inherits all the members of CStr and CIoStr, because each successive generation adds or overrides members from the previous class. Nothing is ever lost (although access rights can sometimes change, as explained later in this chapter). CFontIoStr has one new data member (ptsize) and three new member functions that the other classes do not have. The three classesCStr, CIoStr, and CFontIoStrcreate a class hierarchy, albeit a simple one. The original string class, CStr, is indirectly a base class of CFontIoStr, the most recent class. Through inheritance, CStr passes its contents all the way down to CFontIoStr, much as grandparents pass genes to a grandchild. Figure 8.1 illustrates this simple class hierarchy. Much more complex class hierarchies are possible. You can derive several classes from any given base class, and the result can be a family tree of classes as elaborate as you like.
Without Inheritance: Doing it the Hard WayAlthough inheritance is not the only technique for creating reusable software (nor is it automatically the best technique in all cases), its useful to see how inheritance can save a good deal of work in many cases. Suppose you dont have access to the original CStr source code except for the class declaration. (Consequently, you dont have access to source code for function definitions.) But you want to write a new class, CIoStr, with all the capabilities of CStr plus a couple more. How would you create a CIoStr class without using inheritance and without rewriting all the CStr functions from scratch? You could write such a class without inheritance, but you would have to do the following: write a new class that forms, in effect, a shell around a CStr object; then translate calls to the outer class into calls to the CStr object. Figure 8.2 illustrates this scheme conceptually.
The difficulty of this approach is that every call to a CStr function must be explicitly translated into a call to the CStr object. Heres what the code would look like: #include cstr.h class CIoStr { CStr str; public: // New functions void input(void); void output(void); // Old functions, simulating CStr inheritance char *get(void) const {return str.get(); } int getlength(void) const {return str.getlength();} void cpy(char *s) {str.cpy(s);} void cat(char *s) {str.cat(s);} ...
|
![]() |
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. |