Hiding in Inheritance

Fact zero: In C++, a function is identified with its function name and parameter list. (excluding return type)

Fact one: Inheritance implies that the derived class has at least everything from the base class (if it doesn't define more methods/data members).

Fact two: Hiding can take place when one function in base class has the same name as one function in derived class, such as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base
{
public:
  void print (void)
  {
  ...
  }
}

class Derived : public Base
{
public:
  void print (int i)
  {
  ...
  }
}

// Note: please point it out if I'm wrong with any one of the facts


Now:
Conclusion one: from fact zero and one, I would assume that class Derived has two print functions:
print(void) and print(int);

Conclusion two: from fact two, I would assume that class Derived has only print(int) because print(void) has been hiden.


Which of the two conclusions is correct?

In addition, if conclusion two is correct, is there any way to access the hiden print(void) in class Derived from outside of Derived (e.g. from main function)? Or print(void) has simply disappeared?

Thanks for any inputs.
The Derived class indeed has all functions that the Base class has. However they may not be accessible or they may not be visible, or both. In other words, just because I have 1M bucks, that doesn't mean that other people should know about it, or that other people should be able to access them.

Particularly, to access function from the Base class which is "invisible" in the scope of the Derived class you need to request different scope with the scope resolution operator. Like that:
1
2
3
4
    Derived d;

    d.print(0);
    d.Base::print();

You can also reintroduce all hidden overloads with a given name in the scope of the derived class. This is done like that:
1
2
3
4
5
class Derived : public Base
{
public:
  using Base::print;
...

Note that you can not reintroduce only particular overload. It is all or nothing (and that pisses me off a bit :) Also, it seems that if you reintroduce the overloads in different sections (say before public), then they will have different access control in the scope of the Derived class. To confess to you, the technique is a bit blurry to me.

Regards
Sorry, my post was incorrect. I tried a short example and proved myself wrong. I have thus deleted my response. I apologize if I led anyone astray.
closed account (zwA4jE8b)
I believe that to call the different functions you must create a pointer of that class, then you can use -> to point to that classes print function. also you must use a virtual function.

virtual void print (void); //in the base class

so

void callprint(Base *p);

void main
{

Base *p;
Derived *q;

p = new Base;
q = new Derived;

p->print(); //should print using Baseclassprint
q->print(); //should print using Derivedclassprint

callprint(p);
callprint(q);

}

void callprint(Base *p) //similar to above
{
p->print();
}
Last edited on
closed account (zwA4jE8b)
Heres the example my book uses


The first segment is the base class, the second is the derived class, the third is a sample program using the classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
   
#ifndef H_petTypeVirtual
#define H_petTypeVirtual

#include <string>

using namespace std;

class petType
{
public:
    virtual void print();
    petType(string n = "");

private:
    string name;
};


#endif

//start .cpp

#include <iostream>

#include "petType.h"

using namespace std;

void petType::print() 
{
    cout << "Name: " << name;
}

petType::petType(string n)
{
    name = n;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef H_dogTypeVirtual
#define H_dogTypeVirtual
   
#include <string>
#include "petType.h"

using namespace std;

class dogType: public petType
{
public:
    void print();
    dogType(string n = "", string b = "");

private:
    string breed;
};


#endif

//start .cpp
  
#include <iostream>

#include "dogType.h"

using namespace std;

void dogType::print() 
{
    petType::print();
    cout << ", Breed: " << breed << endl;
}

dogType:: dogType(string n, string b)
               : petType(n)
{
    breed = b;
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  
#include <iostream>
#include "petType.h"
#include "dogType.h"

using namespace std;

void callPrint(petType *p);

int main()
{
    petType *q;                                  //Line 1
    dogType *r;                                  //Line 2

    q = new petType("Lucky");                    //Line 3
    r = new dogType("Tommy", "German Shepherd"); //Line 4

    q->print();                                  //Line 5
    cout << endl;                                //Line 6
    r->print();                                  //Line 7 

    cout << "*** Calling the function callPrint  ***"
         << endl;                                //Line 8
    callPrint(q);                                //Line 9
    cout << endl;                                //Line 10
    callPrint(r);                                //Line 11

    return 0;
}

void callPrint(petType *p)
{
    p->print();
}
Thanks for your explanations, guys.
@sadavied
It's nothing, man. Anyone can make mistakes occasionally.

@CreativeMFS
Your example very clearly illustrated the idea of polymorphism("override phenomenon"). It's a great example. Many thanks for the efforts. However, I'm actually more concerned about the "hiding phenomenon" in inheritance in this thread.

@simeonz
It's almost there! But how do I access the hidden function "void print (void) {...}" from OUTSIDE the derived class?
For example, in main:
1
2
3
4
5
6
int main (void)
{
   Derived son;
   son.print (); //compiler gives me error: no mathcing functiong blablabla...
   son.print (2); //this is perfectly fine
}  


Now the confusion is this:
son is the object of a class derived from Base which is the class of father.
By the meaning of "inheritance", son should automatically has the "void print (void) {...}" function. When I define "void print (int i) {...}" for class Derived, effectively it SHOULD be a case of "overload": i.e. we should be able to call both "son.print ()" & "son.print (2)" in main.

However, this is NOT the case, as the situation here is not "overload", but "hiding", i.e. "void print (void) {...}" seems to have disappeared in the class Derived.

Now my question is: has the "void print (void) {...}" function really disappeared from Derived?
OR
it's still there, but we're no longer able to access it? If it's still there and we're no longer able to access it, is it that we're REALLY no longer able to access it, or there's some other elegant way to access it but simply that I don't know yet?


In fact, to me, this seems to be a deficiency in the C++ language.
Maybe such a thought is due to my ignorance, and if so, please point out the design consideration.

Thank you very much.
h9uest wrote:
But how do I access the hidden function "void print (void) {...}" from OUTSIDE the derived class?
I probably didn't make myself clear, but this is the purpose of the scope resolution operator.

When I define "void print (int i) {...}" for class Derived, effectively it SHOULD be a case of "overload": i.e. we should be able to call both "son.print ()" & "son.print (2)" in main.
Overloading is what happens when you have functions with the same name but different signatures in the same scope. This is not your case. In your case, what happens resembles more function declaration in block scope hiding one from the global scope.

Let me illustrate the analogy. If you have these two declarations in file scope:
1
2
void print(void);
void print(int);
, then you have overloaded the function print.

Now, imagine that the situation is different. Imagine that you have:
1
2
3
4
5
void print(void);
void g() {
    void print(int);
    print(); //ERROR
}
The definition declaration in block scope is not overload of the global one, but hides it instead. Hiding does not require the signatures to match, only the names. In other words, introducing local object hides all global objects with the same name, even if they are functions whose signatures are different. So, what do we do in cases like this, and in any case of name hiding in general? We use fully qualified names that are given with the scope resolution operator. Like this:
1
2
3
4
5
void print(void);
void g() {
    void print(int);
    ::print(); //OK
}

How does this relate to your question, you ask. Well, the situation is very similar. The derived class indeed inherits the function from the base class, but what also happens is that the derived class introduces inner scope where all its declarations (from the body of its class definition) live. Anything declared in the scope of the derived class hides anything declared in the scope of the base class that has the same name. The scope of the derived class is like the scope of the function g in my previous example, and the scope of the base class is analogous to the global scope. Now, trying to access void print(void) directly, like this:
1
2
Derived d;
d.print(); //ERROR 
would produce an error, because this print function has been hidden. So, what do we do, you ask. The same as before, we use the scope resolution operator, but in slightly different form. Like this:
1
2
Derived d;
d.Base::print(); //OK 


In summary:
- all members from the base class are inherited in the derived class
- some of those members are hidden in the derived class, but can still be used by accessing them with the scope resolution operator
- the hidden members can be imported in the scope of the derived class (see my previous post)
- even if a member is not hidden it may be private in the base class and therefore inaccessible; not only to the outside world, but also to the derived class, although it can be overloaded nonetheless
The key terms are visibility and accessibility.

So, you have LSP (Liskov substitution principle), but not on the syntactical level. That is, a template for example may work ok with the base class, but not with the derived class, because of visibility and accessibility issues. But in principle, all functions from the base class apply to the derived class, just as long as you can locate and access them.

I understand your quarrel with the language. You are probably asking yourself the question - with all this language rules, what is the conceptual meaning of two types being in inheritance relationship. But, I ask, what is the meaning of inheritance in general? I mean, equivalence in terms of substitution sounds very solid until you try to dissect it in fine detail and although it is useful idea, it also relatively vague and informal.

Best Regards
Last edited on
@simeonz:
Sincerely, thank you very much. The explanation is very helpful.
closed account (zwA4jE8b)
Would this be and example of hiding???

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#ifndef H_Hiding
#define H_Hiding
#include <iostream>

using namespace std;

class Base
{
	int x, y;
public:
	Base();
	void print(int, int);
};

class Derived: public Base
{
	int a, b;
public:
	Derived();
	void print();
};

Base::Base()
{
	x = 2; y = 4;
}


void Base::print(int a, int b)
{
	cout << "The value of x and y are: " << x << " | " << y << endl;
	cout << "The value of a and b are: " << a << " | " << b << endl;
}

Derived::Derived()
{
	a = 5; b = 10;
}

void Derived::print()
{
	Base::print(a, b);
}
#endif 
Yes, if you are referring to the print declarations. The one in the derived class hides the one in the base class. Your use of scope resolution operator in Derived::print() is what saves the day. Otherwise you would have received a compilation error.

(Notice, that the derived class can still use both functions though. Also, even if a method is not accessible to inheriting classes, because it is declared in private section, it is still there and may be called indirectly from some other method.)

Regards
Last edited on
closed account (zwA4jE8b)
cool, thanks man.

I think I kind of understand the concept.
Topic archived. No new replies allowed.