Why Does an Overridden Function in the Derived Class Hide Other Overloads of the Base Class

Why does an overridden function in the derived class hide other overloads of the base class?

Judging by the wording of your question (you used the word "hide"), you already know what is going on here. The phenomenon is called "name hiding". For some reason, every time someone asks a question about why name hiding happens, people who respond either say that this called "name hiding" and explain how it works (which you probably already know), or explain how to override it (which you never asked about), but nobody seems to care to address the actual "why" question.

The decision, the rationale behind the name hiding, i.e. why it actually was designed into C++, is to avoid certain counter-intuitive, unforeseen and potentially dangerous behavior that might take place if the inherited set of overloaded functions were allowed to mix with the current set of overloads in the given class. You probably know that in C++ overload resolution works by choosing the best function from the set of candidates. This is done by matching the types of arguments to the types of parameters. The matching rules could be complicated at times, and often lead to results that might be perceived as illogical by an unprepared user. Adding new functions to a set of previously existing ones might result in a rather drastic shift in overload resolution results.

For example, let's say the base class B has a member function foo that takes a parameter of type void *, and all calls to foo(NULL) are resolved to B::foo(void *). Let's say there's no name hiding and this B::foo(void *) is visible in many different classes descending from B. However, let's say in some [indirect, remote] descendant D of class B a function foo(int) is defined. Now, without name hiding D has both foo(void *) and foo(int) visible and participating in overload resolution. Which function will the calls to foo(NULL) resolve to, if made through an object of type D? They will resolve to D::foo(int), since int is a better match for integral zero (i.e. NULL) than any pointer type. So, throughout the hierarchy calls to foo(NULL) resolve to one function, while in D (and under) they suddenly resolve to another.

Another example is given in The Design and Evolution of C++, page 77:

class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};

class Derived : public Base{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};

void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}

Without this rule, b's state would be partially updated, leading to slicing.

This behavior was deemed undesirable when the language was designed. As a better approach, it was decided to follow the "name hiding" specification, meaning that each class starts with a "clean sheet" with respect to each method name it declares. In order to override this behavior, an explicit action is required from the user: originally a redeclaration of inherited method(s) (currently deprecated), now an explicit use of using-declaration.

As you correctly observed in your original post (I'm referring to the "Not polymorphic" remark), this behavior might be seen as a violation of IS-A relationship between the classes. This is true, but apparently back then it was decided that in the end name hiding would prove to be a lesser evil.

Overriding overloaded methods hides some of the overloads [duplicate]

your compiler is 100% right.

you overloaded your function to take an integer as argument, then this function hid all of the base class function - so obj calls myMethod(int) be default , yet you don't provide your function an integer.
if you fix your code to be
obj.myMethod(4);

the problem solved.

when overriding a function in the derived class - it hides all the other base class overloads. one can still call the base class with this syntax :
obj.Base::myMethod();

In more in depth answer , this is WHY it happens.
first of all, we need to understand HOW the compiler compiles Object oriented code. remember - the functions compiles into assembly commands and they sit in the code segment. the variables compiles into rows of memory that sit wither in the stack , heap or data segments. functions sits in on part of the application , the variables in a complete different areas. how does the compiler compile a class with variables AND functions? well, it doesn't. the idea goes like this:


let's say a have a class named X with some variables and some functions

1) take all the member functions and bring them out of the class

2) add another argument to each-now-globally-declared functions - const X* this

3) each time you see the syntax x.someFunc(args..) change it to be someFunc(args..,&x)

4) in the functions - if you don't recognize a symbol - try attaching it a this-> and see if you can compile this

5) compile X as C struct and the other as C functions

6)do the same for derived classes
(of course , there is the virtual table issue , but let's stop here)

IN YOUR EXAMPLE:

the psuedo code that might represent the compiler parsed-code is

struct Base{}
struct Derived{}

void myMethod(const Base* this);
void myMethod(int x,const Base* this);
void myMethod(int x, const Derived* this);

//what you tried to do is :
myMethod (&obj);

but the compiler can't find any function that matches these arguments!
this is not very intuitive for someone who don't initially knows how object oriented compiles, but it makes more sense after understanding this compiling procedure.

Overriding only some virtual funtion of base class with the same name [duplicate]

Just add a using declaration inside derived class as shown below. In particular, a using declaration for a base-class member function (like foo) adds all the overloaded instances of that function to the scope of the derived class. Now you can override the version that takes an argument of type int and use the implementation of others that were added in the derived class' scope using the using declaration.

class Derived : public Base
{
public:
//added this using declaration
using Base::foo;
void foo() override {
std::cout << "Derived::foo()" << std::endl;
}
};

Working demo
The output of the above modified program is as you wanted:

Base::foo()
Base::foo(int i)
Derived::foo()
Base::foo(int i)

C++ - Overloading vs Overriding in Inheritance

In C++, any method in a derived class only overrides the method in the base class if their declarations match (I say "match" but I don't know the formal term for that). That is, all arguments must have the same type, and const qualification of this must be the same. If anything there mismatches, the method in the derived class hides all methods with the same name, instead of overriding. This is what the "ERROR" in your picture tries to tell you. So // overrides in a comment in that picture is incorrect and misleading.

Yes, many C++ teachers actually don't understand these somewhat obscure details.

BTW additionally, if you want to override, the method in your base class should be virtual; otherwise, polymorphism won't work. If it was not virtual, we also say that the derived-class method hides the base-class method. Here, however, the part about hiding has almost no meaning; what this term really wants to express is that you're not overriding.

In addition, overloading is, as you noticed, presence of several methods with the same name but different signatures. They should all be present in the derived class to be useful - if the derived class has only one method fa1, and the other fa1 are in the base, they will be hidden. However, there is syntax sugar which "copies" all fa1 from base to derived, disabling all that hiding semantics:

class A
{
public:
void fa1();
void fa1(int);
};

class B: public A
{
public:
using A::fa1;
void fa1(int, int);
};

...
B b;
b.fa1(); // calls A::fa1()
b.fa1(4); // calls A::fa1(int)
b.fa1(4, 8); // calls B::fa1(int, int)

The part about hiding is rarely, if ever, useful. When overriding, you should tell this to your compiler - use the override keyword for that. The compiler will then check that your code works as you intended.

class A
{
public:
virtual void fa1(int) {}
void fa2(int) {}
};

class B: public A
{
public:
void fa1(int) override {} // OK
void fa1() override {} // ERROR: doesn't really override - different signature
void fa2(int) override {} // ERROR: doesn't really override - not virtual in base
};

overloaded functions are hidden in derived class

TTBOMK this doesn't have a real technical reason, it's just that Stroustrup, when creating the language, considered this to be the better default. (In this it's similar to the rule that rvalues do not implicitly bind to non-const references.)

You can easily work around it be explicitly bringing base class versions into the derived class' scope:

class base {
public:
void f(int);
void g(int);
};

class derived : public base {
public:
using base::f;
void f(float);
void g(float); // hides base::g
};

or by calling the explicitly:

derived d;
d.base::g(42); // explicitly call base class version

Change base class fields in derived class with base class method

This code snippet

class Base {
public:
int curr_loc;
Base(int curr_loc):curr_loc(curr_loc)
void reset(int curr_loc){
curr_loc = curr_loc;
}
}

class Derived: public Base{
public:
Derived(int curr_loc):Base(curr_loc)
}

has syntactic errors.

You need to write

class Base {
public:
int curr_loc;
Base(int curr_loc):curr_loc(curr_loc){}
void reset(int curr_loc){
Base::curr_loc = curr_loc;
}
};

class Derived: public Base{
public:
Derived(int curr_loc):Base(curr_loc) {}
};

Pay attention to to the member function reset definition. It should be defined either like

    void reset(int curr_loc){
Base::curr_loc = curr_loc;
}

or like

    void reset(int curr_loc){
this->curr_loc = curr_loc;
}

Otherwise the parameter ciur_loc is assigned to itself in this statement

        curr_loc = curr_loc;

because it hides the data member with the same name of the class Base.



Related Topics



Leave a reply



Submit