VTable和多态性
在阅读了大量有关 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您缺少的是
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
数据成员。仅当您执行以下操作时才会发生切片:What you're missing is that
s
points to aCircle
-- and theCircle
contains a data member calledb
. Whens->draw();
is called, the compiler callsCircle::draw()
, as you recognise, and withinCircle::draw()
, the type of*this
(i.e. the current object) isCircle
notShape
. SoCircle::draw()
has access tob
.EDIT: In answer to your new question,
s
is a pointer to aShape
-- all you're doing is storing the same address (to the start of theCircle
object in memory) but with a different type (Shape*
instead ofCircle*
). The underlyingCircle
object exists in memory regardless of the things pointing to it. You can't accessCircle
-specific data members throughs
directly because it's aShape*
, but the virtual dispatch mechanism means that when you call virtual member functions throughs
, the call gets forwarded to the appropriate member functions inCircle
, i.e.s->draw();
actually ends up invokingCircle::draw
. There's no danger that as a result of storing the address of the underlyingCircle
object in aShape*
, the underlyingCircle
object will be somehow 'sliced', getting rid of theb
data member. Slicing only occurs when you do this sort of thing:sizeof
往往是编译时的事情。它不查看s
指向的对象;它只是看到s
指向Shape
并给出Shape
的大小。信息仍然存在;编译器只是没有向您显示它,因为它没有跟踪s
指向Circle
的事实。您必须将*s
转换回Circle
才能获得正确的大小 - 但这与sizeof(Circle) 是一样的
,我认为这违背了预期目的。s
除了指向一个 Shape 以及如何调用Shape
方法之外,什么都不知道。由于draw
是Shape
上的一个方法,因此可以调用它 - 但由于它是一个虚拟方法,因此该对象有一个查找表,其中显示类似于“对于draw()
,请在此处调用”。对于Circle*
,该表指向Circle::draw
——因此子类的方法被调用。由于指针实际上指向一个Circle
,因此在其余Shape
字段之后有一个b
(仅Circle
code> 及其子类甚至知道存在)。sizeof
tends to be a compile-time thing. It's not looking at the object pointed to bys
; it's just seeing thats
points toShape
s and giving you the size of aShape
. The information's still there; the compiler just isn't showing it to you because it's not keeping track of the fact thats
points to aCircle
. You'd have to cast*s
back toCircle
to get the right size here -- but that'd be the same thing as sayingsizeof(Circle)
, which i'd imagine defeats the intended purpose.s
doesn't know anything except that it points to a Shape, and how to callShape
methods. Sincedraw
is a method onShape
, it can be called -- but since it's a virtual method, the object has a lookup table that says something like "fordraw()
, call here". For aCircle*
, that table points toCircle::draw
-- so the subclass's method gets called. And since the pointer actually points to aCircle
, there's ab
after the rest of theShape
fields (that onlyCircle
and its subclasses even know exists).正如你所说, 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
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
isShape
class. Buts
is pointing to an instance ofCircle
class which naturally has an access to its overridendraw()
method which prints the value ofb
which is also a member ofCircle
. As a part of the question's title, this is polymorphism andsizeof()
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.