When I am learning function overloading, I know that a function differs from another by its name and number of parameters. So I try similar things for inheritance.
I declare a function without parameters in base class, and declare another function with the same name but different number of parameters in derived class.
From my function overloading and inheritance knowledge, the main code should work, but it turns out that the function in the base class is not inherited and I have to explicitly call it.
Could anyone explain to me about this behavior? Shouldn't breathe() be inherited by the derived class? Thanks a lot.
#include <iostream>
using namespace std;
//base class
class animal {
public:
void breathe(){
cout << "animal breathe" << endl;
}
};
//derived class
class fish: public animal {
public:
void breathe(int a){
cout << "fish bubble" << endl;
}
};
int main() {
fish fh;
// correct
fh.breathe(1);
// correct
fh.animal::breathe();
// error
fh.breathe();
return 0;
}
Google method hiding. Add
using animal::breathe;
To class fish
fh.breathe();
is an error message, because there is another breathe() defined in the derived class, which effectively hides the breathe() defined in the base class.
You can correct this by adding the statement:
using animal::breathe;
in the class fish.
Modified fish class:
class fish: public animal {
public:
//Make sure to make this statement public
//otherwise animal::breathe will not be accessible
//to the object of fish.
using animal::breathe;
void breathe(int a){
cout << "fish bubble" << endl;
}
};
When you declare a function in derived class with same name as in base class, you are hiding the base class function. To bring base class function within derived class scope, you have to explicitly specify it using using animal::breath like so:
class fish: public animal {
public:
using animal::breath;
void breathe(int a){
cout << "fish bubble" << endl;
}
};
n3376 10.2/3-4-5
The lookup set for f in C, called S(f, C), consists of two component
sets: the declaration set, a set of members named f; and the subobject
set, a set of subobjects where declarations of these members (possibly
including using-declarations) were found. In the declaration set,
using-declarations are replaced by the members they designate, and
type declarations (including injected-class-names) are replaced by the
types they designate. S(f, C) is calculated as follows:
> If C contains a declaration of the name f, the declaration set
contains every declaration of f declared in C that satisfies the
requirements of the language construct in which the lookup occurs. If
the resulting declaration set is not empty, the subobject set contains
C itself, and calculation is complete.
> Otherwise (i.e., C does not contain a declaration of f or the
resulting declaration set is empty), S(f, C) is initially empty. If C
has base classes, calculate the lookup set for f in each direct base
class subobject B i , and merge each such lookup set S(f, B i ) in
turn into S(f, C).
In your case, there is function breathe in class fish, compiler will not trying to found breathe in base class. You can resolve this by explicitly inserting using declaration in derived class.
using animal::breathe;
Now, compiler has two variant and due overloading resolution animal::breathe will be called.
Related
Consider a diamond inheritance graph (i.e., virtual base class). We know from previous
questions
that on construction the most derived class directly calls the default (0-arg) constructor of the (virtual) base.
But we also know from answers to the previous question (e.g., here
that if the "middle" classes in the diamond have constructors that are used by the most-derived class and those constructors "call" non-default constructors of their (virtual) base class (via the initialization list) then that is not respected … though the bodies of the "middle" classes' constructors are executed.
Why is that? I would have thought it should be a compile error. (Detected, of course, when the most-derived class is declared and the diamond is created.)
I'm looking for two things:
where in the standard is this specified?
does this kind of explicit-yet-ignored code happen anywhere else in the language?
Code sample of what I'm talking about follows its actual and expected outputs below:
B 0arg-ctor
Mf 0arg-ctor
Mt 0arg-ctor
useD
expected output:
ERROR: (line 19) struct `D` creates a diamond inheritance graph where an explicitly
written invocation of a virtual base class constructor is ignored (for base
classes `Mf`and `Mt` and ancestor virtual base class `B`
code:
#include <iostream>
using namespace std;
struct B {
B() noexcept { cout << "B 0arg-ctor" << endl; };
B(bool) noexcept { cout << "B 1arg-ctor" << endl; };
};
struct Mf : public virtual B
{
Mf() : B(false) { cout << "Mf 0arg-ctor" << endl; }
};
struct Mt : public virtual B
{
Mt() : B(true) { cout << "Mt 0arg-ctor" << endl; }
};
struct D : public Mf, public Mt { };
void useD(D) { cout << "useD" << endl; }
int main()
{
D d;
useD(d);
return 0;
}
The rules for initializing bases and members are specified in [class.base.init].
Specifically in p7:
A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
and its complement in p13:
First, and only for the constructor of the most derived class ([intro.object]), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
Hence the initializers B(true) and B(false) are ignored when initializing Mf and Mt because they're not the most derived class, and the initialization of D leads with the initialization of B. No initializer for it is provided, so B() is used.
Making this fail to compile would be basically impossible? To start with, consider:
struct Mf : public virtual B { };
struct D : public Mf { };
That initializes B, but implicitly. Do you want that to be an error for Mf since its initialization would be ignored? I assume no - otherwise this language feature would be completely unusuable. Now, what about:
struct Mf : public virtual B { Mf() : B() { } };
struct D : public Mf { };
Is that an error? It basically means the same thing though. What if Mf had members that needed to be initialized and I, as matter of habit, just like listing the base classes?
struct Mf : public virtual B { Mf() : B(), i(42) { } int i; };
struct D : public Mf { };
Okay, you say, you only error if you actually provide arguments. Which is where a different misconception comes in:
We know from previous questions that on construction the most derived class directly calls the default (0-arg) constructor of the (virtual) base.
That's not true (and is not what those answers state). The most derived class initializes the virtual bases - but this initialization does not have to be default. We could've written:
struct D : public Mf, public Mt { D() : B(true) { } };
And really, there's not an interesting distinction between B() and B(true). Imagine the constructor were just B(bool = true), then does it matter whether or not the user provides the argument true? It would be strange if one were an error but not the other, right?
If you keep going down this rabbit hole, I think you'll find that making this an error would be either exceedingly narrow or exceedingly restrictive.
[class.mi]/7 - For an object of class AA, all virtual occurrences of base class B in the class lattice of AA correspond to a single B subobject within the object of type AA...
[class.base.init]/7 - ... A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
[intro.object]/6 - If a complete object, a data member, or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type or of a non-class type is called a most derived object.
Why is that?
Apart from the obvious; because the standard says so, one possible rationale is that since you only have one base class subobject it doesn't even make sense to allow middle bases to interact with the initialization of the virtual base. Otherwise, which middle base class would you expect to initialize the virtual base, Mt or Mf ?, because for B(false) and B(true) would mean two different way to initialize the same object.
Adding a new class to a codebase should not cause well-formed classes to suddenly become invalid. That would be a language disaster. If Derived initializes its virtual base Base, and it is correct code, then the existence of a further-derived class should have no impact on the validity of Derived. Your expectation would almost completely preclude inheritance from any class simply because it happens to use virtual inheritance somewhere, and make virtual inheritance unusable.
But for the citations you requested (from draft n4762):
10.9.2/13:
In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (6.6.2), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
And the second part you asked about, the virtual base initializer in a non-most-derived class is described here, in 10.9.2/7:
A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
A virtual base is constructed by the most-derived class, but need not use a default constructor to do so, it can use any accessible constructor. Intermediate bases simply do not factor into the construction of a virtual base.
I have two classes related by inheritance:-
class Base
{
public:
virtual void f(int x)
{
cout << "BASE::int" << endl;
}
virtual void f(double x)
{
cout << "BASE::double" << endl;
}
};
class Derived : public Base
{
public:
virtual void f(str::string s)
{
cout << "DERIVED::string" << endl;
}
};
I have provided same method in derived class with different parameters. That means rather than overriding I am hiding base class versions of this function. So, below calls are expected and clear to me.
std::string str("Hello");
Base b;
b.f(1); //calls base class version.
b.f(str); //error.
Derived d;
d.f(1); //error.
d.f(str); //calls derived class version.
But I am not able get clarification for this last scenario.
Base *b = new Derived;
b->f(str); //results in error.
Would compiler not bind this call to derived version of f using vtables and vptrs. But instead it's doing something else. Can anyone provide me complete path how compiler would try to resolve this call as per language mechanisms.
If your pointer is of type Base* then you can only "see" members that are defined in class Base. The compiler doesn't (or pretends not to) "know" that the variable really points to an instance of Derived, even if you just assigned one to it on the previous line.
When you declare a variable to be of type Base*, you're telling the compiler: treat this as something that could point to a Base or to any class derived from it. So you can't access members that are defined in a particular derived class, because there's no guarantee that the pointer actually points to an instance of that derived class.
The vtable only enters the picture at runtime. The generated assembly would have a lookup of the vptr value for a function and a jump to that address. This also means that the polymorphism is "restricted" to functions that Base knows about. Note that this is what makes more sense as well - the definition of a class should only depend on itself and its parents. If you wanted to make Base* b aware of the virtual functions implemented by Derived, you would end up with the number of vtable entries in Bases depending on its children.
In this thread the author of the accepted answer explains why the overridden method in the derived class can not be resolved by the compiler. However the example is relative to a type cast resolution, that is both the base and derived overloaded method have one parameter only and the ambiguity is limited to that parameter type.
But where is the ambiguity when the overloaded methods have a different number of parameters, like in this example?
Note that I'm not asking why the example produces a compile error, I'm asking why the language was designed this way.
#include <iostream>
using namespace std;
class A
{
public:
inline int f(int x) { return x; }
};
class B: public A
{
public:
inline int f(int x, int y) { return x+y; }
};
int main()
{
B b;
cout << b.f(1) << endl; // COMPILE ERROR
cout << b.f(1,2) << endl;
}
The reason you get a compiler error is that f from class A is hidden by the f in class B.
When the compiler does member name lookup, it uses base classes only if the name is not found in the object's class, so it doesn't matter if you have a member in a base class that has a proper parameter list for your call.
From the standard:
10.2.5 Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f, C) is
initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi
,
and merge each such lookup set S(f, Bi) in turn into S(f, C).
In C++, name lookup will stop looking for other names as soon as it find the requested name in one of the base classes.
In your case, the name f is defined in B, so the compiler stop looking in the other base classes.
You can make A::f visible with a using declaration :
class B: public A
{
public:
using A::f;
int f(int x, int y) { return x+y; }
};
The compiler will look for the implementation of function f in class B. The compiler found such an implementation, which has two arguments. You provided only one argument, so there is your error.
How would you take away overloads that make no sense in a derived class? Even in your example, assume that if you have a B instance, you wanted to forbid the use of the single-parameter function. As it is written now, you've removed the single-parameter version (well, at least removed it from name resolution in the context of a B instance). But if you wanted to still have that version available, you can specify using A::F; in your class to bring in the single-parameter version.
I'm getting "hiding" warnings in my compiler because a class inherits from its parent has the same name but different parameters.
Adding a function that merely pushes out a warning to say that this function does nothing (as is true in the base class, but without the warning) that matches the parameters and name of the base class function to the derived class clears this compiler warning. What are the potential knock-on effects of this on the derived class?
EDIT: Assume that I do not wish them to be able to use the base class function. (Don't ask).
Redefining the name in the derived class effectively hides the function in the base class. That's what the warning tells you! :-)
It is a warning, because usually this is a mistake. If it is on purpose, that is ok (but very rare).
The inability of your users to access the base class function through a derived instance without explicitly writing out myDerivedObj.Base::foo(), which they're not likely to do.
Make your function signatures match up, instead, or change the function name.
You need to unhide the base class function in the derived class as:
using Base::Function;
Example:
class Base
{
public:
void Function(int) { cout << "Function(int)" << endl; }
};
class Derived : public Base
{
public:
using Base::Function; //NOTE THIS LINE : Unhiding base class function!
void Function(const char *) { cout << "Function(const char *)" << endl; }
};
Derived d;
d.Function(10); //this calls Base::Function
Demo : http://ideone.com/OTBxg
Please look at the following code:
#include <iostream>
using namespace std;
class A {
public:
A() {};
virtual void foo(double d) { cout << d << endl; }
virtual void foo(double d, int a) = 0;
};
class B : public A {
public:
B() {};
virtual void foo(double d, int a) { cout << d << endl << a << endl; }
};
int main()
{
B b;
b.foo(3.14);
return 0;
}
The compiler (tried g++ and visual c++ 2008) says that there's no function like B:foo(double). The exact message of g++ is:
main.cpp:21: error: no matching function for call to ‘B::foo(double)’
It looks like the effect of hiding rule, but in my opinion the rule should not be used here, since I'm not overriding foo(double) and both foo methods are defined in base class.
I know that I can fix the problem with
using A::foo;
declaration in the derived class B.
Can you explain why the code does not compile and what rules of C++ apply here?
The hiding rule is not about overriding, it is about hiding of names. If the derived class declares a member function, this hides other base class member functions with the same name. This also happens in your case.
Names shadow, not specific functions. Once you make a foo in B, all base foo's (note, by name!) are shadowed.
When the compiler encounters an identifier, the lookup rules kick in and start searching for that identifier. In your concrete situation, with b.foo, the compiler knows that foo must be a member of B or one of its subclasses. The lookup rules state that the compiler must start with the most derived class (considering the static type of the object) and follow up in the hierarchy, and that once the identifier is found in one level only definitions in that level will be considered, it must not keep looking upwards.
B& f(); // might return a B or something derived from B
void test() {
B& b = f(); // static type is B
b.foo(1.0);
}
Regardless of what f returns, the static type is B, so the compiler will lookup in B class and find B::foo(double,int). Since there is no other foo declaration at that level, the compiler must try to match (and fail) the function call with the available method declarations.
The important thing is that the lookup does not look the object but rather looks by type and going upwards, cutting as soon as it encounters the first instance.
Looks perfectly reasonable to me. Although function signature does matter to know what the function is, I can see how this behavior prevents very stupid mistakes.
As ereOn suggested, using directive would be a fair price to pay.