I'm working on C++. Following is my code:
#include <iostream>
using namespace std;
class base
{
public:
virtual void display(int a = 4)
{
cout << "base :: "<< a*a << endl;
}
};
class derived : public base
{
public:
void display(int b = 5)
{
cout << " Derived :: " << b*b*b << endl;
}
};
int main()
{
base *bobj;
derived dobj;
bobj = &dobj;
bobj->display();
return 0;
}
The output is:
Derived :: 64
The function of Base class is called, but default value of the parameter of derived function is used.
Why the derived class method display(), takes the base class method argument value?
Because you're calling it through a pointer to base. That's how it works.
Arguments are pushed on the argument stack (or inside registers) before the actual call. Because you have a pointer to base and no parameters, the default 4 is passed to the function. Then the correct function is called (derived::display), but with base's default parameter. Of course, this is an implementation detail, but the behavior is standard.
C++03 8.4/10
A virtual function call (10.3) uses the default arguments in the declaration of the virtual function determined by the static
type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default
arguments from the function it overrides.
I would provide emphasis on the quote, but the whole thing is pretty self-explanatory.
dobj.display();
would print 125 (5^3).
Default arguments are inserted by the caller. Your code is equivalent to
class base {
public:
virtual void display(int a) { cout << "base :: "<< a*a << endl; }
inline void display(void) { display(4); }
};
etc.
When calling through a base pointer, the default value from the base class is inserted.
The Standard says it all:
(§8.3.6/10) A virtual function call (10.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides. [ Example:
struct A {
virtual void f(int a = 7);
};
struct B : public A {
void f(int a);
};
void m() {
B* pb = new B;
A* pa = pb;
pa->f(); // OK, calls pa->B::f(7)
pb->f(); // error: wrong number of arguments for B::f()
}
— end example ]
Make yourself a less contrived test setup, and it becomes clear:
#include "base.hpp"
int compute(base * p)
{
return p->display();
}
Two things are obvious now:
The default argument can only possibly come from the default argument specified in base.
The actual dispatch is dynamic, since display is a virtual member function.
When you use -> i.e. invoke a function using a pointer it uses the object which is being pointed for taking a decision which in this case is an object of Derived class.
As the specification says...
A virtual function call uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides.
Related
Consider the following code:
#include <iostream>
#include<string>
using namespace std;
class Base
{
public:
virtual string print() const
{
return "This is Base class";
}
};
class Derived : public Base
{
public:
virtual string print() const
{
return "This is Derived class";
}
};
void describe(Base p)
{
cout << p.print() << endl;
}
int main()
{
Base b;
Derived d;
describe(b);
describe(d);
return 0;
}
On executing this code, it gives the output as
This is Base class
This is Base class
I had couple of doubts in this program:
The function print in class Base is a const member function, hence after inheritance, when we try to override the definition of print in Derived class, why doesn't the compiler give an error.
In this program, how we can we pass a derived class object (d) to the function which expects an argument of data Type Base.
Even if we could somehow pass the derived class object to the function, why does it print "This is Base class" instead of "This is Derived Class".
Does implicit type-conversion take place when I pass a derived class object to function describe?
I had be grateful if someone could explain me the working of this code.
The function print in class Base is a const member function, hence after inheritance, when we try to override the definition of print in Derived class, why doesn't the compiler give an error.
There is no error because the program is well-formed. It is unclear why you bring up the detail that the member function is const. Constness of a member function has no effect on whether the function can be overridden. Did you assume that const means final?
In this program, how we can we pass a derived class object (d) to the function which expects an argument of data Type Base.
Because that class Base is a public base of the Derived class, and thus the derived object is implicitly convertible to the base. The result of the conversion is a copy of the base class sub object. This is colloquially called "slicing".
Even if we could somehow pass the derived class object to the function, why does it print "This is Base class" instead of "This is Derived Class".
Because there is no derived class object inside the function. There is a copy of the base.
Does implicit type-conversion take place when I pass a derived class object to function describe?
Yes. When you pass an argument of a type that is different from the type of a non-reference parameter, there is always an implicit conversion.
Indirection is necessary for runtime polymorphism. Try the same using a reference to base as parameter and you'll see the overriding function called.
Your first question is why it did not give you an error because your function is const. Const function does not prohibit you from overriding it. If you want your function to be not overridden by derived classes you can declare that function final.
virtual string print() const final;
Now this will not be overridable by any other derived class. As far as you are concerned about the const. You can Overload your function on the basis of constness. For example, you can have.
virtual string print() const; // Does not modify anything
virtual string print(); Can modify `this`
For the second question can pass the Derived class object to a function witch expect the Base class object. But It is only possible if it is via reference or via a pointer.
void describe(const Base& p);
void describe(Base* p);
For the third question as you passed via copy, so your derived class object is narrowed to Base class. Which essentially loss all the additional information other than its Base class.
The fourth question has the same answer as 3.
This is fully functional code as you had expected to run.
#include <iostream>
#include<string>
using namespace std;
class Base
{
public:
virtual string print() const
{
return "This is Base class";
}
};
class Derived : public Base
{
public:
virtual string print() const override
{
return "This is Derived class";
}
};
void describe(const Base& p)
{
cout << p.print() << endl;
}
int main()
{
Base b;
Derived d;
describe(b);
describe(d);
return 0;
}
Here you can see I declared overridden function override to tell the compiler that this function is being overridden from its Base Class. Without this, it works as expected but it's like function hiding rather than overriding.
In this code I am storing the address of object B in pointer type object of A.
If I call the function then the program must run the function of second class but it doesn't work and run function of 1st class.
If I put virtual before the parental class function then it moves towards other class function.
When I stored 2nd class address in pointer type object, program have to show 2nd class function. But it couldn't do this.
Why this is so?
#include<iostream>
using namespace std;
#include<conio.h>
class a
{
public:
void fun()
{
cout << "1st";
}
};
class b:public a
{
public:
void fun()
{
cout << "2nd";
}
};
class c :public a
{
public:
void fun()
{
cout << "3rd";
}
};
void main()
{
a *A;
b B;
c C;
A = &B;
A->fun();
_getch();
}
When i stored 2nd class address in pointer type object, program have to show 2nd class function...
Not, it does not. In C++ functions are associated with classes (types), not objects. So compiler looks at the type of the pointer your functions is called through. Then, it tries to find such function in the corresponding class definition.
In your case compiler sees "call to the function fun() through the pointer of type A" and it simply translates it to the address of fun() from class A.
*The callable function is stored elsewhere, but not inside the object.
Indeed, this is the point of "statically typed" languages: compiler looks at your object through the prism of the object`s type.
However, as was mentioned, if you want to make association of a function with particular object, you need to use the keyword virtual. This will create a virtual table for each object of class A (and all inherited from A). Then addresses of all functions marked is virtual will be stored inside this virtual table. Consequently, when inheriting class has its own implementation of a virtual function, the address of the new implementation will override the previous address inside the object`s virtual table.
Now, when you call fun() through the pointer of class A the control flow (now during the runtime) will make a lookup to the objects virtual table and will find an address to the function you expect.
class A
{
public:
virtual void fun()
{
cout << "1st";
}
};
class B
{
public:
void func() override
{
cout << "2nd";
}
}
void main()
{
a *A;
b B;
A = &B;
A->fun(); // -> "2nd"
// function address deduced at runtime
}
You need to use virtual keyword, otherwise inheritance might not work as you expected.
So change your class a as follows:
class a
{
public:
virtual void fun()
{
In c++, pointers are static. This means that they are always looking for the parent's functions, even if they are pointing to a child class. You can avoid this by declaring a:fun as virtual. Take a look at this:
http://www.cplusplus.com/doc/tutorial/polymorphism/
The answer to the below output of main is "derived class display with i=10", but I do not understand why? Surely the function was invoked on the base type?
What is the thought-process in determining the answer here?
class base
{
public:
virtual void display(int i = 10)
{
cout<<"Base class display with i = "<<i<<endl;
}
};
class derived : public base
{
public:
void display(int i = 20)
{
cout<<"Derived class display with i = "<< i <<endl;
}
};
int main(int argc, char *argv[])
{
base *bptr = new derived;
bptr->display();
return 0;
}
Have a look at Can virtual functions have default parameters?:
A virtual function call (10.3) uses the default arguments in the
declaration of the virtual function determined by the static type of
the pointer or reference denoting the object. An overriding function
in a derived class does not acquire default arguments from the
function it overrides.
Therefore, bptr->display(); calls the derived version of display, but uses the argument from base, the static type of the pointer bptr.
This is because the default value of an argument has to be determined at compile time while dynamic binding is deferred to the runtime. Using different default arguments in the base and derived versions of the same virtual is almost guaranteed to cause trouble. Problems are likely to arise when the virtual is called through a reference or pointer to base, but the version that is executed is the one defined by the derived. In such cases, the default argument defined for the base version of the virtual will be passed to the derived version, which was defined using a different default argument.
I wanted to have confirmation about the following things:
Virtual Mechanism:
I f I have a base class A and it has a Virtual method, then in the derived class generally, we do not include the virtual statement in the function declaration. But what does a virtual mean when included at the dervied class definition.
class A
{
public:
virtual void something();
}
class B:public A
{
public:
virtual void something();
}
Does, that mean that we want to override the method somethign in the classes that derive from the class B?
Also, another question is,
I have a class A, which is derived by three different classes.Now, there is a virtual method anything(), in the base class A.
Now, if I were to add a new default argument to that method in the base class, A::anything(), I need to add it in all the 3 classes too right.
My pick for the answers:
If a method which is virtual in the base class is redefined in the derived class as virtual then we might mean that it shall be overridden in the corresponding derived classes which uses this class as base class.
Yes.If not overriding does not have any meaning.
Pls let me know if what I feel(above 2) are correct.
Thanks,
Pavan Moanr.
The virtual keyword can be omitted on the override in the derived classes. If the overridden function in the base class is virtual, the override is assumed to be virtual as well.
This is well covered in this question: In C++, is a function automatically virtual if it overrides a virtual function?
Your second question is about default values and virtual functions. Basically, each override can have a different default value. However, usually this will not do what you expect it to do, so my advice is: do not mix default values and virtual functions.
Whether the base class function is defaulted or not, is totally independent from whether the derived class function is defaulted.
The basic idea is that the static type will be used to find the default value, if any is defined. For virtual functions, the dynamic type will be used to find the called function.
So when dynamic and static type don't match, unexpected results will follow.
e.g.
#include <iostream>
class A
{
public:
virtual void foo(int n = 1) { std::cout << "A::foo(" << n << ")" << std::endl; }
};
class B : public A
{
public:
virtual void foo(int n = 2) { std::cout << "B::foo(" << n << ")" << std::endl; }
};
int main()
{
A a;
B b;
a.foo(); // prints "A::foo(1)";
b.foo(); // prints "B::foo(2)";
A& ref = b;
ref.foo(); // prints "B::foo(1)";
}
If all your overrides share the same default, another solution is to define an additional function in the base class that does nothing but call the virtual function with the default argument. That is:
class A
{
public:
void defaultFoo() { foo(1); }
virtual void foo(int n) { .... }
};
If your overrides have different defaults, you have two options:
make the defaultFoo() virtual as well, which might result in unexpected results if a derived class overload one but not the other.
do not use defaults, but explicitly state the used value in each call.
I prefer the latter.
It doesn't matter whether you write virtual in derived class or not, it will always be virtual because of the base class, however it is still better to include virtual to explicitly state that it is virtual and then if you accidentally remove that keyword from base class it will give you compiler error (you cannot redefine non-virtual function with a virtual one). EDIT >> sorry, I was wrong. You can redefine non-virtual function with a virtual one however once it's virtual all derived classes' functions with same signature will be virtual too even if you don't write virtual keyword. <<
If you don't redefine virtual function then the definition from base class will be used (as if it were copied verbatim).
If you wish to specify that a virtual function should be redefined in dervied class you should not provide any implementation i.e. virtual void something() = 0;
In this case your class will be an abstract base class (ABC) and no objects can be instantiated from it. If derived class doesn't provide it's own implementetian it will also be an ABC.
I'm not sure what do you mean about default arguments but function signatures should match so all parameters and return values should be the same (it's best to not mix overloading/default arguments with inheritance because you can get very surprising results for example:
class A
{
public:
void f(int x);
};
class B:public A
{
public:
void f(float x);
};
int main() {
B b;
b.f(42); //this will call B::f(float) even though 42 is int
}
Here is a little experiment to test out what you want to know:
class A {
public:
virtual void func( const char* arg = "A's default arg" ) {
cout << "A::func( " << arg << " )" << endl;
}
};
class B : public A {
public:
void func( const char* arg = "B's default arg" ) {
cout << "B::func( " << arg << " )" << endl;
}
};
class C : public B {
public:
void func( const char* arg ) {
cout << "C::func( " << arg << " )" << endl;
}
};
int main(int argc, char* argv[])
{
B* b = new B();
A* b2 = b;
A* c = new C();
b->func();
b2->func();
c->func();
return 0;
}
result:
B::func( B's default arg )
B::func( A's default arg )
C::func( A's default arg )
conclusion:
1- virtual keyword before A's func declaration makes that function virtual in B and C too.
2- The default argument used is the one declared in the class of pointer/reference you are using to access the object.
As someone pointed out, a function in a derived class with the same name and type signature as a virtual function in the base class is automatically always a virtual function.
But your second question about default arguments is interesting. Here is a tool for thinking through the problem...
class A {
public:
virtual void do_stuff_with_defaults(int a = 5, char foo = 'c');
};
is nearly equivalent to this:
class A {
public:
virtual void do_stuff_with_defaults(int a, char foo);
void do_stuff_with_defaults() { // Note lack of virtual keyword
do_stuff_with_defaults(5, 'c'); // Calls virtual function
}
void do_stuff_with_defaults(int a) { // Note lack of virtual keyword
do_stuff_with_defaults(a, 'c'); // Calls virtual functions
}
};
Therefore you are basically having virtual and non-virtual functions with the same name but different type signatures declared in the class if you give your virtual function default arguments.
On way it isn't equivalent has to do with being able to import names from the base class with the using directive. If you declare the default arguments as separate functions, it's possible to import those functions using the using directive. If you simply declare default arguments, it isn't.
#include <iostream>
class base
{
public:
virtual void print (int a)
{
std::cout << "a: " << a << " base\n";
}
virtual void print (int a, int b)
{
std::cout << "base\n";
}
};
class derived : public base
{
public:
virtual void print (double d)
{
std::cout << "derived\n";
}
};
int main ()
{
int i = 10;
double d = 10000.0;
base *b = new derived ();
b->print (i, i);
b->print (d);
return 0;
}
The output of this function is:
base
a: 10000 base
Why b->print (d) don't invoke the derived class implementation and
performs static cast on 'd' to provide a match with base class
implementation ?
What rule is applied here during virtual function lookup ?
derived::print does not override any member function in base. It is declared as having a single parameter of type double but the two virtual member functions named print in base are declared as having one and two parameters of type int.
When you use b->print(d), only member functions in base are considered during overload resolution, so only void base::print(int) and void base::print(int, int) are considered. void derived::print(double) can't be found because the compiler has no idea that b points to a derived object.
If derived were to override one of the two print functions declared as virtual member functions in base, then that override would be called at runtime.
(On a somewhat related note, derived::print hides the two base::print member functions, so if you were to try to use one of the base class print functions, e.g., derived().print(1, 1), it would fail. You would need to use a using declaration to make those member functions available during name lookup.)
Overload resolution happens at compile time. Overrides happen at run time.
Therefore, the overload resolution of b->print(d); happens first. This selects Base::print(int) because it's the only one-argument print.
At runtime, b points to a Derived object that has no override for Base::print(int). Therefore, Base::print(int) is still called.
Because double can be automatically converted to an int in the first definition it sees (in the base class)
See explicit keyword or this question