VTable和多态性

发布于 2024-10-29 04:50:11 字数 2059 浏览 1 评论 0原文

在阅读了大量有关 VTable 的内容后,我仍然有一个未解答的问题。

给定下一类:

#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;
    }
};

和以下测试:

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();
}

我得到这个输出:

default Shape ctor
Circle constructor
size of *c is 12
size of *s is 8
print Circle. The number is 5

我的问题是:我知道 s 如何寻址 Circle::draw,但是 s 如何知道变量 b=5? 正如此测试所示,s 没有此信息。我在这里缺少什么?

谢谢!

好吧,伙计们。感谢您的快速回答...

我从您的回答中了解到,Circle::draw() (*this) 是 Circle 类型。好的。 我的问题现在变成了这样:因为我只希望 s 成为 Shape* 类型,也就是说,我的程序中只需要 Shape 品质。接下来的 4 个字节(Circle 中的 b 变量)是否有可能被编译器以某种方式获取?如果是这样,显然 Circle::draw() 将无法按预期工作。

如果不是,编译器如何知道我将在 s “结束”之后需要接下来的 4 个字节?

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 ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

莫多说 2024-11-05 04:50:11

您缺少的是 s 指向 Circle - 并且 Circle 包含一个名为 b 的数据成员>。当调用 s->draw(); 时,编译器会调用 Circle::draw(),如您所知,并且在 Circle::draw( )*this(即当前对象)的类型是Circle而不是Shape。因此,Circle::draw() 可以访问b

编辑:在回答你的新问题时,s是一个指向Shape指针——你所做的就是存储相同的地址(到内存中 Circle 对象的开头),但类型不同(Shape* 而不是 Circle*)。无论指向它的对象是什么,底层的 Circle 对象都存在于内存中。您无法直接通过 s 访问特定于 Circle 的数据成员,因为它是 Shape*,但虚拟调度机制意味着当您调用通过s的虚拟成员函数,调用被转发到Circle中相应的成员函数,即s->draw();实际结束调用Circle::draw。由于将底层 Circle 对象的地址存储在 Shape* 中,底层 Circle 对象会以某种方式存在,因此不会有任何危险 ' sliced',去掉 b 数据成员。仅当您执行以下操作时才会发生切片:

Circle c;
Shape s = c; // copies the Shape data members across from c, but slices off the rest

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
那支青花 2024-11-05 04:50:11
  1. sizeof 往往是编译时的事情。它不查看 s 指向的对象;它只是看到 s 指向 Shape 并给出 Shape 的大小。信息仍然存在;编译器只是没有向您显示它,因为它没有跟踪 s 指向 Circle 的事实。您必须将 *s 转换回 Circle 才能获得正确的大小 - 但这与 sizeof(Circle) 是一样的,我认为这违背了预期目的。

  2. s 除了指向一个 Shape 以及如何调用 Shape 方法之外,什么都不知道。由于 drawShape 上的一个方法,因此可以调用它 - 但由于它是一个虚拟方法,因此该对象有一个查找表,其中显示类似于“对于 draw(),请在此处调用”。对于Circle*,该表指向Circle::draw——因此子类的方法被调用。由于指针实际上指向一个 Circle,因此在其余 Shape 字段之后有一个 b(仅 Circle code> 及其子类甚至知道存在)。

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

雪化雨蝶 2024-11-05 04:50:11

正如你所说, s 指向实例 Circle 而不仅仅是 Circle 实例的绘制方法。因此 Circle 中的方法可以访问 Circle 的实例变量。因此,当调用 Circle::draw 时,它可以“看到”实例变量,因为它是 Circle 的成员

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

如果没有你 2024-11-05 04:50:11

sizeof() 给出传递的参数类型的大小。在本例中,*s 的类型是 Shape 类。但是s指向Circle类的实例,它自然可以访问其重写的draw()方法,该方法打印的值>b 也是Circle 的成员。作为问题标题的一部分,这是多态性,而 sizeof() 与多态性无关。

对于 typeid() 函数也是如此,它适用于参数的类型,而不是参数的值或参数所指向的对象。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文