开发者

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


  1. sizeof tends to be a compile-time thing. It's not looking at the object pointed to by s; it's just seeing that s points to Shapes and giving you the size of a Shape. The information's still there; the compiler just isn't showing it to you because it's not keeping track of the fact that s points to a Circle. You'd have to cast *s back to Circle to get the right size here -- but that'd be the same thing as saying sizeof(Circle), which i'd imagine defeats the intended purpose.

  2. s doesn't know anything except that it points to a Shape, and how to call Shape methods. Since draw is a method on Shape, it can be called -- but since it's a virtual method, the object has a lookup table that says something like "for draw(), call here". For a Circle*, that table points to Circle::draw -- so the subclass's method gets called. And since the pointer actually points to a Circle, there's a b after the rest of the Shape fields (that only Circle 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜