Source of name_hiding1.cpp


  1: /** @file name_hiding1.cpp
  2: Based on an example from Eckel, Thinking in C++, 2nd edition.
  3: */

  5: #include <iostream>
  6: #include <string>
  7: using namespace std;

  9: class Base
 10: {
 11: public:
 12:     int f() const
 13:     {
 14:         cout << "\nExecuting Base::f() with int return type.";
 15:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 16:         return 1;
 17:     }

 19:     int f(string s) const
 20:     {
 21:         cout << "\nExecuting Base::f(string) with int return type.";
 22:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 23:         return 1;
 24:     }

 26:     void g()
 27:     {
 28:         cout << "\nExecuting Base::g() with void return type.";
 29:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 30:     }
 31: };


 34: class Derived1 : public Base
 35: {
 36: public:
 37:     void g() const
 38:     {
 39:         cout << "\nExecuting Derived1::g() with void return type.";
 40:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 41:     }
 42: };


 45: class Derived2 : public Base
 46: {
 47: public:
 48:     //Redefinition:
 49:     int f() const
 50:     {
 51:         cout << "\nExecuting Derived2::f() with int return type.";
 52:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 53:         return 2;
 54:     }
 55: };


 58: class Derived3 : public Base
 59: {
 60: public:
 61:     //Change return type:
 62:     void f() const
 63:     {
 64:         cout << "\nExecuting Derived3::f() with void return type.";
 65:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 66:     }
 67: };


 70: class Derived4 : public Base
 71: {
 72: public:
 73:     //Change argument list:
 74:     int f(int) const
 75:     {
 76:         cout << "\nExecuting Derived4::f(int) with int return type.";
 77:         cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');
 78:         return 4;
 79:     }
 80: };


 83: int main()
 84: {
 85:     cout << "\nThis program illustrates the hiding of overloaded names "
 86:         "during inheritance.";
 87:     cout << "\nPress Enter to continue ... ";  cin.ignore(80, '\n');

 89:     string s("Hello");
 90:     int n;

 92:     Derived1 d1;
 93:     n = d1.f();
 94:     d1.f(s);

 96:     Derived2 d2;
 97:     n = d2.f();
 98:     //d2.f(s);    //1Compiler error: string version hidden
 99:     //error C2660: 'Derived2::f' : function does not take 1 arguments

101:     Derived3 d3;
102:     d3.f();
103:     //n = d3.f(); //2Compiler error: return int version hidden
104:     //error C2440: '=' : cannot convert from 'void' to 'int'

106:     Derived4 d4;
107:     //n = d4.f(); //3Compiler error: f() version hidden
108:     //error C2660: 'Derived4::f' : function does not take 0 arguments

110:     n = d4.f(1);
111: }

113: /*
114: Output
115: ------

117: This program illustrates the hiding of overloaded names during inheritance.
118: Press Enter to continue ...

120: Executing Base::f() with int return type.
121: Press Enter to continue ...

123: Executing Base::f(string) with int return type.
124: Press Enter to continue ...

126: Executing Derived2::f() with int return type.
127: Press Enter to continue ...

129: Executing Derived3::f() with void return type.
130: Press Enter to continue ...

132: Executing Derived4::f(int) with int return type.
133: Press Enter to continue ...
134: Press any key to continue . . .
135: */

137: /*
138: Notes:
139: If you inherit a class and provide a new definition for one of its
140: member functions, there are two possibilities:
141: - The new function has exactly the same signature and return type
142:   in the derived class as in the base class. This is called
143:   -- "redefining" for ordinary member functions
144:   -- "overriding" for virtual base class member functions

146: What exactly is the situation if you change the member function
147: argument list or return type? The above example illustrates ...

149: In Base you see an overloaded function f().

151: Derived1 makes no changes to f() but does redefine g().
152: In main we see that both overloaded versions of f() are available
153: in Derived1.

155: Derived2 redefines one overloaded version of f() but not the other.
156: The result is that the second overloaded form is not available.

158: Derived3 changes the return type of f() and hides *both* of the
159: Base class versions of f().

161: Derived4 changes the parameter list of f() and also hides both of
162: the Base class versions of f().

164: So, in general, whenever you redefine an overloaded function name
165: from a base class in a derived class, all the other versions of
166: that function are hidden in the new class.

168: But also note carefully the following:
169: If you change the interface of the base class by modifying the signature
170: and/or the return type of a member function from a base class, then you
171: are using the class in a different way than inheritance in normally
172: intended to support. This may or may not be "wrong", it's just that
173: the ultimate goal of inheritance is to support *polymorphism*, and if
174: you change the function signature you are actually changing the interface
175: of the base class.

177: If that's what you actually intended, then you are using inheritance
178: simply to reuse code, and not to maintain the common interface of the
179: base class (an essential aspect of polymorphism). In general, when you
180: use inheritance this way, it means you are taking a general purpose
181: class and specializing it for a particular need, which may mean that
182: what you really need to be using is composition (making an object of
183: the new class a data member of the original class), rather than
184: inheritance.
185: */