Click to See Complete Forum and Search --> : question about Inheritance


zameericle
June 28th, 2002, 05:56 AM
Specifically why does declaring a method in a derived class hide all methods of the same name in the base class?

for instance why doesn't the following work:



class X {
virtual void foo() { cout << "foo" << endl; };
},

class Y : public x {
void foo( int x ) { cout << x << endl;
};

void Test() {
Y yClass;

y.foo( 2 ); // okay works
y.foo( ); // Error. X::foo() is hidden by Y::foo( int )
}



intuitively one would think that ALL methods from a base class are inherited by a derived class, unless the derived class overloads the method, in which case ALL methods except the overloaded method is inherited.

does anyone know why the behavior is different?

thanks

Zameer

Elrond
June 28th, 2002, 06:57 AM
If you want to use the function of X, you have to tell the compiler:
((X)y).foo();

The why is probably to look in the object oriented concepts.

dude_1967
June 28th, 2002, 09:46 AM
Elrond's suggestion to "cast" the instantiation of Y to type X will compile and run. However some programmers would consider this to be bad style.

Your question actually touches on some critical aspects of the theory of object orientation, as Elrond suggested. Particularly interesting to you (although not apparent from your coding example) might be to study-up on polymorphism of pointers.

Please note that your definitiion of void foo(int) in Y is, in fact, not an overwrite of virtual void foo(void) in X. In order to overwrite a function, both the return value as well as all input parameters must be identical.

You also forgot to specify the access type of foo in each class definition. By failing to specify either public, protected, or private, the default is private. Y's inheritance of "public X" will only inherit the public member functions (of which there are none).

Here is a corrected, full listing of your sample code.

#include <iostream>
#include <string>
using namespace std;

class X
{
public:
virtual void foo() { cout << "foo" << endl; };
};

class Y : public X
{
public:
void foo( int x ) { cout << x << endl; }
};

void Test()
{
Y y;

y.foo( 2 ); // okay works
((X) y).foo( ); // "cast" y to type X
}

int main(int argc, char* argv[])
{
Test();
return 1;
}

:)

jfaust
June 28th, 2002, 10:40 AM
An understanding of polymorphism does not explain why a derived class method hides a base class method with the same name but different signature. I find that somewhat annoying myself.

I'm sure there is a specific reason why this is. Maybe it would be too difficult to handle by the compiler. Maybe it leads to an ambiguity. I'm not sure.

Anybody have an explanation for this?

Jeff

Graham
June 28th, 2002, 02:58 PM
Consider the following:

struct B
{
void f(int);
};

struct D : B
{
void f(double);
void g();
};

void D::g()
{
f(3);
};

Which f() should the compiler call? D::f() can be called by converting the integer actual argument, but B::f() is a closer match. What if f(int) was a global function, rather than a member of B?

What the compiler does is this:

1) it looks in the immediate scope (in the above case the scope of struct D) and makes a list of all the functions with the given name (f in this case). It takes no notice of accessibility or argument lists.

2) If it finds no functions at all with that name it moves to the next enclosing scope (B in the example above), and repeats from 1) until it runs out of scopes.

Also, note that "virtual" is a red herring in the original example.

BTW the solution to the OP's problem is:

class X {
public:
virtual void foo() { cout << "foo" << endl; };
};

class Y : public x {
public:
void foo( int x ) { cout << x << endl;
};

void Test() {
Y y;

y.foo( 2 ); // okay works
y.X::foo( ); // OK: specifies the scope
}


Explanation taken from "Exceptional C++" by Herb Sutter.

jfaust
June 28th, 2002, 03:08 PM
"I can see clearly now, the rain is gone."

Thanks Graham,

Jeff

Graham
June 28th, 2002, 03:25 PM
Thanks, Herb :p

Graham
June 28th, 2002, 04:35 PM
Here's a slightly more subtle one:

struct B
{
void f() const;
};

struct D : B
{
void f();
};

void g(const D& d)
{
d.f(); // Oops!
}

zameericle
July 1st, 2002, 03:23 AM
thanks Graham for the explanation...

I guess my problem is that I think that when a class is inherited, it receives all the methods made available from the superclass. This, imho, should mean that one need not explicitly qualify where the method comes from.

Maybe the comipler should search for an exact match amongst a list of all available functions.. but I can see that this too would create a lot of problems... hmmm maybe there is no exact solution..

oh well..

Graham
July 1st, 2002, 04:41 AM
Exactly - I think that, in this case, C++ implements the simplest rule: a given name hides all occurrences of that name in enclosing scopes. No caveats or conditions to remember.