I read about the inheritance mechanism in C++ and about virtual functions.
according to my knowlendge (in all examples I have encountered), inherited methods had the same signature as the parent class'.
My question is the following:
I know that function default parameter value is not a part of function signature.
Can I define this value to be some constant in the parent Class virtual function, and in the derived class declare and implement the overriding method without this default value.
In this case, when I call the derived object's method using a pointer to parent class, will the function be called with/without this default initializion?
thanks
Default arguments are mostly syntactic sugar and get determined at compile time. Virtual dispatch, on the other hand, is a run-time feature. It would probably be least surprising to have that default parameter chosen that was defined alongside with the function that actually gets called but this is not possible (at least not without additional run-time overhead) for the reason stated above.
Therefore, the default parameter is selected by the compiler using the static type of the object a member function is called upon. Let's see an example.
#include <iostream>
#include <memory>
class Base
{
public:
virtual void
f(int a, int b = 1)
{
std::cout << "Base: a = " << a << ", b = " << b << "\n";
}
};
class Derived : public Base
{
public:
virtual void
f(int a = 1, int b = 2) override
{
std::cout << "Derived: a = " << a << ", b = " << b << "\n";
}
};
int
main()
{
std::unique_ptr<Base> base_as_base {new Base {}};
std::unique_ptr<Base> derived_as_base {new Derived {}};
std::unique_ptr<Derived> derived_as_derived {new Derived {}};
base_as_base->f(0); // Base: a = 0, b = 1
derived_as_base->f(0); // Derived: a = 0, b = 1
// derived_as_base->f(); // compiler error
derived_as_derived->f(0); // Derived: a = 0, b = 2
derived_as_derived->f(); // Derived: a = 1, b = 2
}
I agree that this is confusing. Please don't write code like this. Fortunately, there is a simple workaround. Apart from not using default parameters at all, we can use an idiom called non-virtual interfaces. The virtual function is made protected and not given any default parameters. It is then only called indirectly by a non-virtual function from the base class. That function can have all default parameters defined in a single place.
#include <iostream>
#include <memory>
class Base
{
public:
void
f(int a, int b = 1)
{
this->impl(a, b);
}
protected:
virtual void
impl(int a, int b)
{
std::cout << "Base: a = " << a << ", b = " << b << "\n";
}
};
class Derived : public Base
{
protected:
virtual void
impl(int a, int b) override
{
std::cout << "Derived: a = " << a << ", b = " << b << "\n";
}
};
int
main()
{
std::unique_ptr<Base> base_as_base {new Base {}};
std::unique_ptr<Base> derived_as_base {new Derived {}};
std::unique_ptr<Derived> derived_as_derived {new Derived {}};
base_as_base->f(0); // Base: a = 0, b = 1
derived_as_base->f(0); // Derived: a = 0, b = 1
derived_as_derived->f(0); // Derived: a = 0, b = 1
}
This is what I found from the C++ Draft Standard N3337:
8.3.6 Default arguments
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 ]
Coming to your question:
Can I define this value to be some constant in the parent Class virtual function,
Yes
and in the derived class declare and implement the overriding method without this default value
Yes.
However, when you call the function using a derived class pointer, you'll have to provide an argument. When you call the function using a base class pointer, you don't need to provide an argument.
Related
I have following code snippet:
class base
{
public:
virtual void print(char a){ std::cout << " Base\n"; }
};
class derived : public base
{
public:
void print(float a) { std::cout << " Derived\n"; }
};
int main() {
base* d = new derived;
d->print(1.5);
}
Output is Base.
Why is the output coming from the base function and not from the derived one?
You have not overridden the function in the base class, you have overloaded it: The version in the derived class takes a float as an argument, and is quite a different beast from the base class method which takes a char. Moreover, the float version in the derived class shadows the base class version: The base class version becomes inaccessible for calls on the derived type.
As such, the following code
Derived d;
d.print('a');
d.print(1.5);
will print
Derived
Derived
because the call is resolved by the compiler to the only version of print() that's available in Derived.
Likewise, when you call d->print(1.5) through a pointer to Base, the derived class' version is inaccessible to the call: The compiler looks at the base class, sees that there is no print() method defined with a float argument, and converts the argument to char instead. It then calls the only implementation of print(char), which happens to be supplied by the base class.
If you simply change the signature of the print() method in the derived class to match that of the base class, the odd behavior will go away.
When you declare Base* d = new Derived;, the type of the class is Base, as printed by typeid(d).name(), so this instance doesn't have access to child class methods. If you change the type to Derived, you'll call the child method:
#include <iostream>
#include <typeinfo>
class Base
{
public:
virtual void print(char a) {
std::cout << " Base " << std::endl;
}
};
class Derived : public Base
{
public:
void print(float a) {
std::cout << " Derived " << std::endl;
}
};
int main()
{
Derived* d = new Derived;
std::cout << "class type is: " << typeid(d).name() << std::endl;
d->print(1.5);
return 0;
}
Output:
class type is: P7Derived
Derived
Furthermore, declaring the parent class print method virtual doesn't allow an instance of Base to call the child version of print because the child hasn't overridden it (different headers). Creating an instance of Base with Base *d = new Derived; and changing the Derived print method header to void print(char a) in the Derived class would allow you to call the child print method and output Derived, even from an instance of Base, using the virtual keyword.
#include <iostream>
#include <typeinfo>
class Base
{
public:
virtual void print(char a) {
std::cout << " Base " << std::endl;
}
};
class Derived : public Base
{
public:
void print(char a) {
std::cout << " Derived " << std::endl;
}
};
int main()
{
Base* d = new Derived;
std::cout << "class type is: " << typeid(d).name() << std::endl;
d->print(1.5);
return 0;
}
Output:
class type is: P4Base
Derived
I have a problem with a final function. I want to "stop" the polymorphism in a class but I still want to generate the same function in a derived class.
Something like this:
class Base{
protected:
int _x, _y;
public:
Base(int x = 0, int y = 0) : _x(x), _y(y){};
int x() const { return _x; }
int y() const { return _y; }
virtual void print()const{ cout << _x*_y << endl; }
};
class Derived : public Base{
public:
Derived(int x = 0, int y = 0) : Base(x, y){}
void print()const final { cout << _x*_y / 2.0 << endl; } // final inheritance
};
class NonFinal : public Derived{
void print()const{ cout << "apparently im not the last..." << endl }
// here i want a new function. not overriding the final function from Derived class
};
I think this is an experimental question, since actually you should rethink what you are doing when you require to "override a final function" (sounds contradicting, doesn't it?).
But you could introduce a "dummy"-parameter, i.e. void NonFinal::print(int test=0)const, which let's the compiler treat the member function as a different one. Not sure if that solves your "problem"; but at least it introduces a function with the same name, which can still be called without passing an argument, and which is separated from the ones of Derived and Base.
class NonFinal : public Derived{
public:
void print(int test=0)const{ cout << "apparently im not the last..." << endl; }
};
int main() {
Base b (10,10);
Derived d (20,20);
NonFinal nf;
Base *bPtr = &d;
bPtr->print(); // gives 200
bPtr = &nf; // gives 0
bPtr->print();
nf.print(); // gives "apparantly..."
}
Sorry, but it's not possible to create a function in a derived class when a function with the same name exists as final in the base class. You'll need to rethink your design.
The problem stems from the fact that a function declaration in a derived class with the same name as a function in a base class is treated as an attempt to override whether the override keyword is present or not (for historical reasons, I presume). So you can't "turn off" overriding.
Here's a relevant standard quote:
§ 10.3/4 [class.virtual]
If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed. [ Example:
struct B {
virtual void f() const final;
};
struct D : B {
void f() const; // error: D::f attempts to override final B::f
};
—end
If I declare a base class (or interface class) and specify a default value for one or more of its parameters, do the derived classes have to specify the same defaults and if not, which defaults will manifest in the derived classes?
Addendum: I'm also interested in how this may be handled across different compilers and any input on "recommended" practice in this scenario.
Virtuals may have defaults. The defaults in the base class are not inherited by derived classes.
Which default is used -- ie, the base class' or a derived class' -- is determined by the static type used to make the call to the function. If you call through a base class object, pointer or reference, the default denoted in the base class is used. Conversely, if you call through a derived class object, pointer or reference the defaults denoted in the derived class are used. There is an example below the Standard quotation that demonstrates this.
Some compilers may do something different, but this is what the C++03 and C++11 Standards say:
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()
}
Here is a sample program to demonstrate what defaults are picked up. I'm using structs here rather than classes simply for brevity -- class and struct are exactly the same in almost every way except default visibility.
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
using std::stringstream;
using std::string;
using std::cout;
using std::endl;
struct Base { virtual string Speak(int n = 42); };
struct Der : public Base { string Speak(int n = 84); };
string Base::Speak(int n)
{
stringstream ss;
ss << "Base " << n;
return ss.str();
}
string Der::Speak(int n)
{
stringstream ss;
ss << "Der " << n;
return ss.str();
}
int main()
{
Base b1;
Der d1;
Base *pb1 = &b1, *pb2 = &d1;
Der *pd1 = &d1;
cout << pb1->Speak() << "\n" // Base 42
<< pb2->Speak() << "\n" // Der 42
<< pd1->Speak() << "\n" // Der 84
<< endl;
}
The output of this program (on MSVC10 and GCC 4.4) is:
Base 42
Der 42
Der 84
This was the topic of one of Herb Sutter's early Guru of the Week posts.
The first thing he says on the subject is DON'T DO THAT.
In more detail, yes, you can specify different default parameters. They won't work the same way as the virtual functions. A virtual function is called on the dynamic type of the object, while the default parameter values are based on the static type.
Given
class A {
virtual void foo(int i = 1) { cout << "A::foo" << i << endl; }
};
class B: public A {
virtual void foo(int i = 2) { cout << "B::foo" << i << endl; }
};
void test() {
A a;
B b;
A* ap = &b;
a.foo();
b.foo();
ap->foo();
}
you should get
A::foo1
B::foo2
B::foo1
This is a bad idea, because the default arguments you get will depend on the static type of the object, whereas the virtual function dispatched to will depend on the dynamic type.
That is to say, when you call a function with default arguments, the default arguments are substituted at compile time, regardless of whether the function is virtual or not.
#cppcoder offered the following example in his [closed] question:
struct A {
virtual void display(int i = 5) { std::cout << "Base::" << i << "\n"; }
};
struct B : public A {
virtual void display(int i = 9) override { std::cout << "Derived::" << i << "\n"; }
};
int main()
{
A * a = new B();
a->display();
A* aa = new A();
aa->display();
B* bb = new B();
bb->display();
}
Which produces the following output:
Derived::5
Base::5
Derived::9
With the aid of the explanation above, it is easy to see why. At compile time, the compiler substitutes the default arguments from the member functions of the static types of the pointers, making the main function equivalent to the following:
A * a = new B();
a->display(5);
A* aa = new A();
aa->display(5);
B* bb = new B();
bb->display(9);
As other answers have detailed, its bad idea. However since no one mentions simple and effective solution, here it is: Convert your parameters to struct and then you can have default values to struct members!
So instead of,
//bad idea
virtual method1(int x = 0, int y = 0, int z = 0)
do this,
//good idea
struct Param1 {
int x = 0, y = 0, z = 0;
};
virtual method1(const Param1& p)
As you can see from the other answers this is a complicated subject. Instead of trying to do this or understand what it does (if you have to ask now, the maintainer will have to ask or look it up a year from now).
Instead, create a public non-virtual function in the base class with default parameters. Then it calls a private or protected virtual function that has no default parameters and is overridden in child classes as needed. Then you don't have to worry about the particulars of how it would work and the code is very obvious.
This is one that you can probably figure out reasonably well by testing (i.e., it's a sufficiently mainstream part of the language that most compilers almost certainly get it right and unless you see differences between compilers, their output can be considered pretty well authoritative).
#include <iostream>
struct base {
virtual void x(int a=0) { std::cout << a; }
virtual ~base() {}
};
struct derived1 : base {
void x(int a) { std:: cout << a; }
};
struct derived2 : base {
void x(int a = 1) { std::cout << a; }
};
int main() {
base *b[3];
b[0] = new base;
b[1] = new derived1;
b[2] = new derived2;
for (int i=0; i<3; i++) {
b[i]->x();
delete b[i];
}
derived1 d;
// d.x(); // won't compile.
derived2 d2;
d2.x();
return 0;
}
while reading through vptr and vtable concept i got this wonderful piece of code, but i am not able to make out the concept involved here:
#include <iostream>
using namespace std;
class A
{
public:
virtual void foo(int x = 10)
{
cout << "base x : " << x << "\n";
}
virtual void bar()
{
cout << "base bar\n";
}
};
class B : public A
{
public:
virtual void foo(int x = 20)
{
cout << "derived x : " << x << "\n";
}
private:
virtual void bar()
{
cout << "derived bar\n";
}
};
class C : public B
{
};
int main()
{
A x; x.foo(); // x.foo(10);
B y; y.foo(); // x.foo(20);
A *p(&y);
p->foo();
}
now the output i get here is:
base x : 10
derived x : 20
derived x : 10
how is it possible that even when derived x(i.e B::foo()) is being printed then the default argument is that of base function(i.e A::foo())?
C++ standard section 8.3.6 point 10 mentions that:
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.
In your example the evaluation of default argument is done on the basis of type of "p" which is "A". Hence the evaluation of default argument is done from the declaration of A, and the calling of function happens by the usual lookup in vptr table.
It seems that the default parameters are resolved at compile time. See here and here.
The default values that are used will be those defined in the static (compile-time) type. So if you were to change the default parameters in an override, but you called the function through a base class pointer or reference, the default values in the base would be used.
I have an abstract base class and a pair of classes derived from this base class. I would like to introduce a static const member that has a different value between the two derived classes but the same value for all instances of a given derived class.
I have code which uses a base class pointer assigned to an instance of one of the two derived classes so that I can easily switch between derived classes by changing what the base class pointer is assigned to. I would like to be able to obtain the value of the derived class' constant value using the base class in a similar way so that I can easily switch between the two classes. The desired behavior looks something like this:
#include <iostream>
using namespace std;
// Abstract base class
class A {
protected:
int c; // this is really a constant and should also be static
public:
static int s;
int get_c() const {
return this->c;
}
virtual int foo() = 0; // makes this class abstract
};
class B : public A {
public:
static const int sb = 10;
B() {
this->c = 1;
}
int foo() {
return -1;
}
};
class C : public A {
public:
static const int sc = 20;
C() {
this->c = 2;
}
int foo() {
return -2;
}
};
int main() {
B btest;
C ctest;
A *ptr = &btest; // pointer to instance of B
//cout << ptr->c << endl; // would fail compilation (c is protected)
cout << "B's c = " << ptr->get_c() << endl;
cout << "B's foo() returns " << ptr->foo() << endl;
cout << "Accessing B's static const member: " << B::sb << endl;
ptr = &ctest; // pointer to instance of C
//cout << ptr->c << endl; // would fail compilation (c is protected)
cout << "C's c = " << ptr->get_c() << endl;
cout << "C's foo() returns " << ptr->foo() << endl;
cout << "Accessing C's static const member: " << C::sc << endl;
return 0;
}
In the above code sb and sc are the static const members I want, but the problem with them is that base class A has no knowledge of them. The base class member c is doing what I want but is not static const as desired (I've made c a protected member so that it cannot be modified, but if I can declare it const then it can be public). This code has the desired behavior from c:
I can get a distinct value for c from each derived class using a base class pointer.
c is effectively constant since it is protected and I provide no setter function.
However, c isn't really constant in that it isn't const and it isn't static so every instance has an unnecessary copy of it.
Is there a way to get the desired behavior and also declare c with static const like sb and sc?
You can't implement this with a static variable in A; that will be common to all subtypes of A, so you can't get a different value for each subtype. Since the dynamic type of the object behind a base-class pointer is only known at run-time, you'll need a run-time mechanism to get the value from it.
This could use a per-object variable, which can be const if you initialise it in the constructor:
class A {
// ...
const int c;
A(int c) : c(c) {}
};
class B : public A {
// ...
B() : A(1) {}
};
class C : public A {
// ...
C() : A(2) {}
};
or a virtual function that each subclass overrides to return a different value:
class A {
// ...
int get_c() const = 0;
};
class B : public A {
// ...
int get_c() const {return 1;}
};
class C : public A {
// ...
int get_c() const {return 2;}
};
Both options meet both your requirements; one has the cost of a virtual function call on access, while the other has the cost of a per-object variable. You'll have to decide which cost is more suitable for your needs.