VTable and Polymorphism
After reading alot about VTables, I still have one unanswered question.
Given the next class:
#include <iostream>
using namespace std;
class Shape {
public:
int* a;
Shape(){
cout<<"default Shape ctor"<<endl;
a = new int(15); // default
}
Shape(int n){
开发者_JAVA技巧 a = new int(n);
cout<<"Shape(n) constructor"<<endl;
}
// copy constructor
Shape(const Shape& s){
cout<<"copy constructor"<<endl;
a = new int(*(s.a));
}
Shape& operator=(const Shape& s){
cout<<"operator="<<endl;
if (&s == (this))
return (*this);
// this.clear();
a = new int(*(s.a));
return (*this);
}
virtual void draw(){
cout<<"print Shape the number is "<<*a<<endl;
};
virtual ~Shape(){
delete a;
cout<<"Shape distructor"<<endl;
}
};
class Circle : public Shape {
public:
int b;
Circle() {
cout<<"Circle constructor"<<endl;
b=5;
}
virtual void draw() {
cout<<"print Circle. The number is "<<b<<endl;
}
~Circle(){
cout<<"Circle distructor"<<endl;
}
};
and the following test:
static void test2(){
Circle* c = new Circle();
cout<<"size of *c is "<<sizeof(*c)<<endl;
Shape* s = c;
cout<<"size of *s is "<<sizeof(*s)<<endl;
s->draw();
}
I get this output:
default Shape ctor
Circle constructor
size of *c is 12
size of *s is 8
print Circle. The number is 5
My question is: I know how s addresses Circle::draw, but how can s know the variable b=5? As this test show, s doesn't have this information. What am I missing here?
Thanks!
OK guys. Thanks for your quick answers...
I've learned from your answers that in Circle::draw() (*this) is of type Circle. OK. My question now has changed to this: Because I only wanted s to be a Shape* type, that is, I needed in my program only the Shape qualities. Is it possible that the next 4 bytes (the b variable in Circle) will be taken by the compiler somehow? If so, obviously Circle::draw() will not work as expected..
If not, how does the compiler knows that I will need these next 4 bytes after the "end" of s ?
What you're missing is that s
points to a Circle
-- and the Circle
contains a data member called b
. When s->draw();
is called, the compiler calls Circle::draw()
, as you recognise, and within Circle::draw()
, the type of *this
(i.e. the current object) is Circle
not Shape
. So Circle::draw()
has access to b
.
EDIT: In answer to your new question, s
is a pointer to a Shape
-- all you're doing is storing the same address (to the start of the Circle
object in memory) but with a different type (Shape*
instead of Circle*
). The underlying Circle
object exists in memory regardless of the things pointing to it. You can't access Circle
-specific data members through s
directly because it's a Shape*
, but the virtual dispatch mechanism means that when you call virtual member functions through s
, the call gets forwarded to the appropriate member functions in Circle
, i.e. s->draw();
actually ends up invoking Circle::draw
. There's no danger that as a result of storing the address of the underlying Circle
object in a Shape*
, the underlying Circle
object will be somehow 'sliced', getting rid of the b
data member. Slicing only occurs when you do this sort of thing:
Circle c;
Shape s = c; // copies the Shape data members across from c, but slices off the rest
sizeof
tends to be a compile-time thing. It's not looking at the object pointed to bys
; it's just seeing thats
points toShape
s and giving you the size of aShape
. The information's still there; the compiler just isn't showing it to you because it's not keeping track of the fact thats
points to aCircle
. You'd have to cast*s
back toCircle
to get the right size here -- but that'd be the same thing as sayingsizeof(Circle)
, which i'd imagine defeats the intended purpose.s
doesn't know anything except that it points to a Shape, and how to callShape
methods. Sincedraw
is a method onShape
, it can be called -- but since it's a virtual method, the object has a lookup table that says something like "fordraw()
, call here". For aCircle*
, that table points toCircle::draw
-- so the subclass's method gets called. And since the pointer actually points to aCircle
, there's ab
after the rest of theShape
fields (that onlyCircle
and its subclasses even know exists).
As you say, s points to instance Circle and not just the draw method of instance of Circle. Therefore the methods in Circle have access to the instance variables of Circle. So when Circle::draw is called, it can "see" the instance variables as it is a member of Circle
sizeof()
gives the size of the type of the parameter passed. In this case type of *s
is Shape
class. But s
is pointing to an instance of Circle
class which naturally has an access to its overriden draw()
method which prints the value of b
which is also a member of Circle
. As a part of the question's title, this is polymorphism and sizeof()
has nothing to do with polymorphism.
This is also true for typeid()
function, works with the type of the parameter, not the value of parameter or the object, parameter is pointed to.
精彩评论