Search code examples
c++inheritancevtable

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


Solution

  • 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