Understanding C++ Virtual Methods of Instances Allocated on the Stack
For the following code:
#include<iostream>
using namespace std;
class A{
public:
virtual int f(){return 1;}
};
class B : public A{
public:
virtual int f(){return 2;}
};
int main(int argc,char*argv[]){
A b=B();
cout<<b.f()<&l开发者_StackOverflow中文版t;endl;
}
I expect the number 2
to be printed. Instead the program prints the number 1
.
Could someone explain why this is?
What you've done is called slicing. See What is object slicing?
Instead, pointers should be used:
A* b = new B();
The problem is that the type of your variable is A
, and not B
. The line:
A a = B();
creates a temporary of type B
and then copies the A
subpart of it into a
that is of type A
.
To use an object polymorphically you need to handle it through a pointer or reference:
int main() {
B b;
A &a = b; // an A reference to the B object
A *p = &b; // an A pointer to a B object
a.f(); // will dispatch to B::f
p->f(); // will dispacth to B::f
}
This is due to slicing. Runtime polymorphism only works through pointer or reference in C++. You can get virtual dispatch with another variable:
B b;
A& a = b;
cout << a.f() << endl;
Or you can directly assign a pointer, like here:
A* aptr = new B;
aptr->f();
delete aptr;
but here class B
needs a virtual destructor. Though not strictly required in the first case, providing virtual destructor is a general rule for polymorphic types. GCC -Wnon-virtual-dtor
flag helps you catch this error.
As others have stated, this is a case of slicing. The type of 'b' is 'A'. It is initialised via the implicitly defined default copy constructor for A:
A::A(const A& other);
This initialisation is valid because 'B' is derived from 'A'; i.e. it is an 'A'. Try implementing the copy constructor for 'A' explicitly to help understand what is going on.
Note that one practice to avoid this kind of issue is to ensure that all non-leaf classes are abstract.
You are observing what is called object slicing in c++. You can alleviate by using a pointer to the base:
int main(int argc,char*argv[]){
B b;
A* a=&b;
cout<<a->f()<<endl;
}
If you convert the stack variables into pointers, you will see that you get the correct answer. This has to do with the fact that b (an instance of A) is allocated on the stack, but remains type A, despite the fact that you copied B() over it.
#include<iostream>
using namespace std;
class A
{
public: virtual int f()
{
return 1;
}
};
class B : public A
{
public:
virtual int f()
{
return 2;
}
};
int main(int argc,char*argv[])
{
A * b = new B();
cout << b->f() << endl;
delete b;
}
精彩评论