****SOS***神啊救救我吧,怎么想了老久都想不通啊50分基分答好番翻,

nuke505 2005-04-11 12:23:41
前提:我知道实际上基类和其派生类的VPTR是相同的,即他们存放VPTR的地址是同一个。但按这个原理的话下面的程序我有点不懂。
#include<iostream>
using namespace std;
//------------------------------------------------------------
class Base
{
....
virtual fun(){cout<<"this is Base:: fun();}
};
//----------------------------------------------------------------
class Derive:public Base
{
...
virtual fun(){cout<<"this is Derive::fun();}
};
//-------------------------------------------------------------------
int main
{
Base *pd=new Derive();//当程序运行到这行时,Derive的构造函数将把VPTR初始化为指向Derive的VTABLE首地址,

Base *pb=new Base();//而当程序运行到这行时,Base的构造函数又将把VPTR初始化为指向Base的VTABLE首地址,这是VPTR的值不在是上面那个指向Derive的VTABLE首地址。

pd->fun();//因此调用这行时,pd查找vptr时会得到指向Base的vtable的首地址,那么它将调用Base下的fun();
return 0;
}
//我知道事实上执行的结果不是这样的,但是我不知道我的推理上哪出了问题,但是可以肯定的是我的前提没错,就是实际上基类和其派生类的VPTR是相同的,即他们存放VPTR的地址是同一个,但各个类却有不同的vtable.
谁能渗透的帮我讲讲。
...全文
307 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
nuke505 2005-04-11
  • 打赏
  • 举报
回复
TO:每个类都有自己的vptr,都是指向自己的vtabl
那只是你们逻辑上的认识,实际在硬件发生上,&vptr是相同的,我编了个程序取出基类和派生类的&vptr看过,确实是这样的。不信你们自已编个程序验证一下。这个地址是相同的。
nuke505 2005-04-11
  • 打赏
  • 举报
回复
是吗前提错了吗?我仔细看了关于这方面的介绍,包括硬件上的,你可以看看下面这个FAQ里面关于虚函数调用时硬件的介绍章节,他里面讲的就是&vptr是相同的,不管是基类的还是派生类的只要是同一个继承线路。
http://www.parashift.com/c++-faq-lite/virtual-functions.html
lemon520 2005-04-11
  • 打赏
  • 举报
回复
每个类都有自己的vptr,都是指向自己的vtabl
nuke505 2005-04-11
  • 打赏
  • 举报
回复
我是说vptr只有一个,即&vptr是相同的,不管是基类的还是派生类的,那么这一个&vptr中怎么可能装入两个地址来指分别指向Base的vtable和Derive的vtable呢?
lemon520 2005-04-11
  • 打赏
  • 举报
回复
前提都错了
nuke505 2005-04-11
  • 打赏
  • 举报
回复
40107D jmp Base::fun (004013c0)

004013A0 mov dword ptr [eax],offset Base::`vftable' (0042f03c)
这是在编译时感觉有价值的两行,根据这两行我只能肯定Base::fun的地址是这个: 004013c0
但后面的汇编看不懂了,是不是vftable所存放的地址是在0042f03c啊,如果是的话那么vptr指向的就应该是这个了。那vptr本身被存放的地址也就是说&vptr可不可以从上面看出啊,是40107D 吗?因为它里面存的Base::fun (004013c0)这条指令。
找个懂汇编的帮我看一下
Rodge 2005-04-11
  • 打赏
  • 举报
回复
发现还是有笔误的地方再次订正:

但是对于派生类而言它的表里会保护没有重写的基类的虚函数
改为:
但是对于派生类而言它的表里会保留没有重写的基类的虚函数

订正:
创建一个的VTBL包含了派生的和基类的.
改为:
创建一个包含了派生的虚函数以及没有被派生类重写的基类虚函数的VTBL.

订正:
创建一个VTBL.
改为:
创建一个只有基类虚函数的VTBL。

看样子中文真的应该恶补了!
Rodge 2005-04-11
  • 打赏
  • 举报
回复
订正:
它只会创建一张表包含了派生类的,已经没有重写的基类的虚函数。
改为:
它只会创建一张表包含了派生类的虚函数,以及没有被派生类重写的基类的虚函数。
Rodge 2005-04-11
  • 打赏
  • 举报
回复
老大,我实在弄不清楚是你没有理解这个英文原意还是我没有理解!

它上面有这一段:
// Your original C++ source code

class Base {
public:
...
FunctionPtr* __vptr; ← supplied by the compiler, hidden from the programmer
...
};

当然这个是伪代码,表示每个基类都一个指针成员。

我的理解:由于不是静态的,因而实例化的时候vptr本身的地址肯定是不一样的。(这点你肯定是能同意吧?)

In step #1, the compiler creates a hidden v-table, keeping the same function-pointers as in Base::__vtable but replacing those slots that correspond to overrides.

我的理解:在说派生类的时候它是用CREATES这个单词,表示也会创建一个表。但是这个表会有基类的函数指针,但是如果派生类自己重写了某个函数,就会取代基类的那个函数指针

This is not a second v-pointer; it's the same v-pointer that was defined in the base class, Base; remember, the compiler does not repeat step #2 in class Der

我想引起你矛盾的主要就是这段话了!

我的理解:由于派生类里本身不会定义FunctionPtr* __vptr这个成员,只有基类才定义。但是派生来是从基类派生出来的,所以派生类用的还是基类的那个成员,基类是public的,所以说是同一个指针。

对于一个派生类,它不会建两张表---一张基类的本身,一张派生的本身。他们只有一个地址!!

我再重复一遍:对于一个派生类,它不会建两张表---一张基类本身的虚函数表,一张派生本身的虚函数表。他们只有一个地址!!

它只会创建一张表包含了派生类的,已经没有重写的基类的虚函数。


综合起来理解:每个类都有各自的一张表,(是不是每个类的实例同用一张表暂时没有验证),但是对于派生类而言它的表里会保护没有重写的基类的虚函数。

因此:
Base *pd=new Derive();
创建一个的VTBL包含了派生的和基类的.

Base *pb=new Base();
创建一个VTBL.

这是个人理解,如有不对,还望大家指正!本人中英文都没有学好,一知半解!
lemon520 2005-04-11
  • 打赏
  • 举报
回复
楼主:您的测试代码是错误的。
再次强调一下,虚函数会导致编译器在对象的前面插入一个vptr,增加4个字节的开销,实际上vptr的地址就是对象的地址。
建议您看看《深度探索c++对象模型》
lonenomad 2005-04-11
  • 打赏
  • 举报
回复
VTBL *vtbl //VTBL是一个函数指针,vtbl就是一个指向函数指针的指针,也就是说vtbl中存的是一个函数指针的地址。它能说明什么啊?????????
VTBL *vtbl = (VTBL*)*(int*)vptr; 这是一条赋值语句,无论右边是什么,对于左边来说都是一个函数指针的地址,vptr是什么啊,不过是个形参而已。
别外必须说的是:无论是基类还是派生类,他们的指针都不会直接指向虚表,他们必须通过对象中的虚表指针。才能找到虚表。
whyglinux 2005-04-11
  • 打赏
  • 举报
回复
To nuke505 ()

你程序中的vtbl变量是一个局部自动变量,&vtbl是表示这个变量在内存中的地址,绝对不是函数表的地址。vtbl的值其实就是对象的地址,当然不可能是虚拟函数表的地址或者其它,云云。

函数表、指向函数表的虚函数指针成员是由编译器内部自动实现的,在程序中不可能得到它们的信息。如果想得到它们的信息,你可以在程序中设置端点,在Debug状态下观察。
ljq14 2005-04-11
  • 打赏
  • 举报
回复
指针肯定不一样,但是指向同一区域???
ljq14 2005-04-11
  • 打赏
  • 举报
回复
顶下~~
lonenomad 2005-04-11
  • 打赏
  • 举报
回复
楼主的检证程序是错误的。
基类的虚表和派生类的虚表,用DEBUG一看就知,我看了,不一样。
healer_kx 2005-04-11
  • 打赏
  • 举报
回复
Base *p1 = new Base;
》?

啥道理? Base幸好不是抽象的。
nuke505 2005-04-11
  • 打赏
  • 举报
回复
TO:lemon520(喷血)

下面这个就是测试&vptr的源代码,有兴趣就看看吧
//----------------------------------
#include <iostream>
using std::cout;
using std::endl;

class Base {
public:
virtual void zuu() { cout << "Base::zuu" << endl; }
virtual void foo() { cout << "Base::foo " << ", a = " << a <<endl; }
virtual void gaa(int c) { cout << "Base::gaa"<< ", a = " << a << ", c = " << c << endl; }
Base():a(123) { };
protected:
int a;
};

class Derived : public Base
{
public:
virtual void zuu(){ cout << "Derived::zuu" << endl; }
virtual void foo() { cout << "Derived::foo " << ", a = " << a << ", b = " << b << endl; }
Derived():b(456) { };
private:
int b;
};

void mycall_noPara(void *p);


int main()
{
Base *p1 = new Base;
Derived *p2 = new Derived;
mycall_noPara(p1);
mycall_noPara(p2);
cout << "d1 Size is = " << sizeof(*p1) << endl;
cout << "d2 Size is = " << sizeof(*p2) << endl;

system("pause");
}

void mycall_noPara(void *vptr)
{
typedef void (*VTBL)(); // Virtual Table
VTBL *vtbl = (VTBL*)*(int*)vptr; // dereference the vptr, initialize vtbl
VTBL * * pf=&vtbl;
cout<<"the vtbl is:"<<vtbl<<endl;//这儿是输出每次调用此函数时显示该类下的函数指针数组的首地址,即vtable的首地址不同类会不同
cout<<"the pf is:"<<pf<<endl;//这儿是输出每次调用此函数时显示该类下的&vptr,不同类但属同一条继承线路的所有类是相同的 ,上面的p1和p2是属于不同的类但是调用此函数和会显示出他们的vptr是相同的。

}

ljq14 2005-04-11
  • 打赏
  • 举报
回复
我都糊涂了~~~~
每个类实例中都有自己的虚表指针吧~~~~
lonenomad 2005-04-11
  • 打赏
  • 举报
回复
哎......无语。
lemon520 2005-04-11
  • 打赏
  • 举报
回复
虚函数会导致在每个对象增加4个字节的vptr开销。既然每个对象都有vptr,怎么可能基类和派生类的vptr会是同一个呢?
你说的取出基类和派生类的&vptr是一样的,把程序贴出来看看。
加载更多回复(1)

64,682

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧