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){
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