5

在阅读了很多关于 VTables 的内容后,我仍然有一个未解决的问题。

给定下一节课:

#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 个字节?

4

4 回答 4

2

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

编辑:在回答你的新问题时,s是一个指向a的指针Shape——你所做的只是存储相同的地址(到Circle内存中对象的开头),但类型不同(Shape*而不是Circle*)。Circle无论指向它的东西如何,底层对象都存在于内存中。你Circle不能通过直接访问特定的数据成员,s因为它是一个Shape*,但是虚拟调度机制意味着当你通过调用虚拟成员函数时s,调用会被转发到适当的成员函数Circle,即s->draw();实际上最终调用了Circle::draw。由于将底层Circle对象的地址存储在Shape*,底层Circle对象将以某种方式“切片”,摆脱b数据成员。切片仅在您执行此类操作时发生:

Circle c;
Shape s = c; // copies the Shape data members across from c, but slices off the rest
于 2011-04-01T10:39:53.953 回答
1
  1. sizeof往往是编译时的事情。s它不是在看;指向的对象 它只是看到s指向Shapes 并给你 a 的大小Shape。信息还在;编译器只是没有向您显示它,因为它没有跟踪s指向Circle. 您必须*s回退才能Circle在此处获得正确的尺寸-但这与说的相同sizeof(Circle),我认为这会破坏预期的目的。

  2. s除了它指向一个形状以及如何调用Shape方法之外,什么都不知道。由于draw是 on 方法Shape,因此可以调用它——但由于它是虚拟方法,因此该对象有一个查找表,上面写着“for draw(), call here”之类的内容。对于 a Circle*,该表指向Circle::draw-- 所以子类的方法被调用。而且由于指针实际上指向 a Circle,因此b在其余Shape字段之后有 a (只有Circle它及其子类甚至知道存在)。

于 2011-04-01T10:39:53.647 回答
0

sizeof()给出传递的参数类型的大小。在这种情况下,类型*sShape类。但是s指向一个Circle类的实例,该实例自然可以访问其覆盖的draw()方法,该方法打印的值b也是Circle. 作为问题标题的一部分,这是多态性,sizeof()与多态性无关。

对于函数也是如此typeid(),它与参数的类型一起工作,而不是参数的值或对象,参数被指向。

于 2011-04-01T11:27:04.463 回答
0

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

于 2011-04-01T10:41:22.917 回答