/******************************************************************/
#include <iostream>
#include <iomanip>
using namespace std;
class base
{
public :
virtual void f() {cout<<"this is 'base'"<<endl;}
};
class inher1:public base
{
public :
virtual void f() {cout<<"this is 'inher1'"<<endl;}
};
void main()
{
base *b_ptr=new inher2;//产生一个对象
void (*p)(base *)=0; //声明一个函数指针
long tlb_ptr=0; //一个长整形变量,用于储存32位的地址
memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
p(b_ptr);
delete b_ptr;
}
/******************************************************************/
在这里,小弟想问的是
我把:
memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
改为:
memcpy(&p,b_ptr,sizeof(long));
后为什么出错了?
我尝试打印出 tlb_ptr,b_ptr,p 的“值”,发现三者互不相同,也是不明白,请各位大侠指教!
13 个解决方案
#1
函数指针比较特别,因为它们之间没有必然的联系,所以就不会产生隐式的指针转换。
故而不能用static_cast、dynamic_cast及const_cast来进行转换,只能使用reinterpret_cast强制实现转换。
b_ptr的指针类型比较隐晦,因为它是由编译器做出来的,所以其类型不可知。推测应该是
(void *)[]而不是void *。
故而不能用static_cast、dynamic_cast及const_cast来进行转换,只能使用reinterpret_cast强制实现转换。
b_ptr的指针类型比较隐晦,因为它是由编译器做出来的,所以其类型不可知。推测应该是
(void *)[]而不是void *。
#2
tlb_ptr的值是对象里的vptr的值,即vtbl的地址
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
如有不明白,我再想办法画个图来说明,留言,或者加QQ 510857
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
如有不明白,我再想办法画个图来说明,留言,或者加QQ 510857
#3
将 memcpy(&p,b_ptr,sizeof(long)); 改为:
memcpy(&p,reinterpret_cast<void *>((*reinterpret_cast<long*>(b_ptr))),sizeof(long));
因为:memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址!
那肯定使 p 得不到正确指向!
memcpy(&p,reinterpret_cast<void *>((*reinterpret_cast<long*>(b_ptr))),sizeof(long));
因为:memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址!
那肯定使 p 得不到正确指向!
#4
to :BluntBlade(无锋之刃)
能讲解一下在
memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
中指针的转换过程吗?
主要是不明白 ‘类对象’, ‘函数’,‘普通数据’ 的‘指针’ 有何区别, 为何要通过long类型变量作为中间量,才能取得虚函数表中真正的虚函数的地址?
to inline(虚函数):
为何说 memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址?
新的地址 指的是怎么样的地址呢?
能讲解一下在
memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
中指针的转换过程吗?
主要是不明白 ‘类对象’, ‘函数’,‘普通数据’ 的‘指针’ 有何区别, 为何要通过long类型变量作为中间量,才能取得虚函数表中真正的虚函数的地址?
to inline(虚函数):
为何说 memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址?
新的地址 指的是怎么样的地址呢?
#5
b_ptr base: A base's vbtl
vptr ptr to f()
+-------+ +-------+ +-------+
| &A | -> | &vbtl | -> | &f() |
+-------+ +-------+ +-------+
&表示内存空间中的值为某个对象的地址
memcpy的函数原型是memcpy(void *,void *,int)
先看memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy得到了目标地址(tbl_ptr的地址,long *类型,可以隐式转换为void *)
和源地址(b_ptr的值,即A的地址,base *类型,可以隐式转换为void *)。
因此得以调用memcpy。
执行memcpy后tlb_ptr开始的四字节内存中储存了&A开始的四字节内存原来的值,
即对象A的vptr的值,也就是vbtl的地址(因为base中没有定义数据成员,而定义
有虚函数,所以A中只有vptr)。
再看memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
memcpy得到了目标地址(p的地址, void (*)(base *),可以转换为void *)和源
地址(tbl_ptr的值,long类型,不能隐式转换为void *),故而不能调用memcpy。
于是必须通过reinterpret_cast<void *>(tlb_ptr)将tlb_ptr的值的类型long转化
为void *,这样就满足了memcpy的调用要求。
由上面的分析也可以得到:
tlb_ptr的值是对象里的vptr的值,即vtbl的地址
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
vptr ptr to f()
+-------+ +-------+ +-------+
| &A | -> | &vbtl | -> | &f() |
+-------+ +-------+ +-------+
&表示内存空间中的值为某个对象的地址
memcpy的函数原型是memcpy(void *,void *,int)
先看memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy得到了目标地址(tbl_ptr的地址,long *类型,可以隐式转换为void *)
和源地址(b_ptr的值,即A的地址,base *类型,可以隐式转换为void *)。
因此得以调用memcpy。
执行memcpy后tlb_ptr开始的四字节内存中储存了&A开始的四字节内存原来的值,
即对象A的vptr的值,也就是vbtl的地址(因为base中没有定义数据成员,而定义
有虚函数,所以A中只有vptr)。
再看memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
memcpy得到了目标地址(p的地址, void (*)(base *),可以转换为void *)和源
地址(tbl_ptr的值,long类型,不能隐式转换为void *),故而不能调用memcpy。
于是必须通过reinterpret_cast<void *>(tlb_ptr)将tlb_ptr的值的类型long转化
为void *,这样就满足了memcpy的调用要求。
由上面的分析也可以得到:
tlb_ptr的值是对象里的vptr的值,即vtbl的地址
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
#6
inline兄的说法是:
b_ptr的值是对象的地址,*b_ptr的值就是vptr的值,即vbtl的地址。
memcpy(&p,b_ptr,sizeof(long));
可以通过编译,因为base *可以隐式地转换为void *。
这样p中的值就是vptr的值,即vbtl的地址,而不是函数的地址了。
单继承已经足够复杂,如果是多继承就更头大了。
如果楼主有心情的话可以去看《Inside The C++ Object Model》,里面有详尽的说明。
请指教。
b_ptr的值是对象的地址,*b_ptr的值就是vptr的值,即vbtl的地址。
memcpy(&p,b_ptr,sizeof(long));
可以通过编译,因为base *可以隐式地转换为void *。
这样p中的值就是vptr的值,即vbtl的地址,而不是函数的地址了。
单继承已经足够复杂,如果是多继承就更头大了。
如果楼主有心情的话可以去看《Inside The C++ Object Model》,里面有详尽的说明。
请指教。
#7
非常感谢 BluntBlade(无锋之刃) 。
现在终于明白原来传给 tlb_ptr 的是虚函数表的头地址,而不是那实际虚函数的地址。
看来我对指针的理解还不够深啊!
现在终于明白原来传给 tlb_ptr 的是虚函数表的头地址,而不是那实际虚函数的地址。
看来我对指针的理解还不够深啊!
#8
让我来解释一下你的行为吧:
memcpy(&tlb_ptr,b_ptr,sizeof(long));
将基类指针 b_ptr 所指对象 4 个单元内容复制到 long 型对象 tlb_ptr 中,
现在的 tlb_ptr 与 b_ptr 所指的对象其实完全相同,不过 tlb_ptr 需要重新解释
才能当作类对象使用,否则使用 tlb_ptr 只能当一个值来用!
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
reinterpret_cast<void *>(tlb_ptr) 将 tlb_ptr 的 值解释成地址!
乍一看好象这个地址是由类对象的位域"凑"成的!怎么能赋值给 p 呢?
但实际上并没有错,因为你的类中有虚函数,类中有一个"虚函数表",
这个虚函数表是一个指针数组,而 p 得到的是这个指针数组的首址!
总之,没有特别需要不要使用 reinterpret_cast
即类中声明的第一个虚函数的地址,所以下面的调用才能进行!
假设类中没有虚函数,那么无论你怎么重新解释后面的调用都将是没有定义的!!!
memcpy(&tlb_ptr,b_ptr,sizeof(long));
将基类指针 b_ptr 所指对象 4 个单元内容复制到 long 型对象 tlb_ptr 中,
现在的 tlb_ptr 与 b_ptr 所指的对象其实完全相同,不过 tlb_ptr 需要重新解释
才能当作类对象使用,否则使用 tlb_ptr 只能当一个值来用!
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
reinterpret_cast<void *>(tlb_ptr) 将 tlb_ptr 的 值解释成地址!
乍一看好象这个地址是由类对象的位域"凑"成的!怎么能赋值给 p 呢?
但实际上并没有错,因为你的类中有虚函数,类中有一个"虚函数表",
这个虚函数表是一个指针数组,而 p 得到的是这个指针数组的首址!
总之,没有特别需要不要使用 reinterpret_cast
即类中声明的第一个虚函数的地址,所以下面的调用才能进行!
假设类中没有虚函数,那么无论你怎么重新解释后面的调用都将是没有定义的!!!
#9
指针这玩意儿,够呛。楼主好好研究一下咯。
不过老实说,指针就是C/C++乃至计算机的灵魂级事物。
不过老实说,指针就是C/C++乃至计算机的灵魂级事物。
#10
谢谢各位老大。
不过,小弟还想问一个问题,就是我将我所给出的原码修改了一下,就是在base和inher1类中
加入其它的成员(私有,公有)数据与函数,在vc6.0下发觉程序依然正常运行,而且结果不变。这是否意味着某个类若有虚函数表,则表的头地址必定在类对象指针所指的内存地址
上(即“最开头”)???
不过,小弟还想问一个问题,就是我将我所给出的原码修改了一下,就是在base和inher1类中
加入其它的成员(私有,公有)数据与函数,在vc6.0下发觉程序依然正常运行,而且结果不变。这是否意味着某个类若有虚函数表,则表的头地址必定在类对象指针所指的内存地址
上(即“最开头”)???
#11
若某个类中有虚函数则会生成一个函数指针数组即虚函数表!
且在这个类对象的开头生成一个指针指向这个虚函数表的首址!
这些虚函数名被解释成为虚函数表的下标!
转换顺序先为从基类继承的虚函数,倘若本类中重新定义了则
本类中的覆盖从基类继承的!然后是在本类中顺序声明的虚函数!
但这个虚函数表不一定在类对象的体内!
你加一些非虚函数成员不影响结果!你在基类和派生类中加一些
虚函数试试!然后改变一下声明的顺序试试!再试一下派生类中
不重新定义基类的虚函数!你会明白的!
且在这个类对象的开头生成一个指针指向这个虚函数表的首址!
这些虚函数名被解释成为虚函数表的下标!
转换顺序先为从基类继承的虚函数,倘若本类中重新定义了则
本类中的覆盖从基类继承的!然后是在本类中顺序声明的虚函数!
但这个虚函数表不一定在类对象的体内!
你加一些非虚函数成员不影响结果!你在基类和派生类中加一些
虚函数试试!然后改变一下声明的顺序试试!再试一下派生类中
不重新定义基类的虚函数!你会明白的!
#12
vptr的位置是由编译器厂商决定的。不同的编译器有不同的现实方法,当然效率也不同。
#13
多谢各位大侠!
#1
函数指针比较特别,因为它们之间没有必然的联系,所以就不会产生隐式的指针转换。
故而不能用static_cast、dynamic_cast及const_cast来进行转换,只能使用reinterpret_cast强制实现转换。
b_ptr的指针类型比较隐晦,因为它是由编译器做出来的,所以其类型不可知。推测应该是
(void *)[]而不是void *。
故而不能用static_cast、dynamic_cast及const_cast来进行转换,只能使用reinterpret_cast强制实现转换。
b_ptr的指针类型比较隐晦,因为它是由编译器做出来的,所以其类型不可知。推测应该是
(void *)[]而不是void *。
#2
tlb_ptr的值是对象里的vptr的值,即vtbl的地址
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
如有不明白,我再想办法画个图来说明,留言,或者加QQ 510857
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
如有不明白,我再想办法画个图来说明,留言,或者加QQ 510857
#3
将 memcpy(&p,b_ptr,sizeof(long)); 改为:
memcpy(&p,reinterpret_cast<void *>((*reinterpret_cast<long*>(b_ptr))),sizeof(long));
因为:memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址!
那肯定使 p 得不到正确指向!
memcpy(&p,reinterpret_cast<void *>((*reinterpret_cast<long*>(b_ptr))),sizeof(long));
因为:memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址!
那肯定使 p 得不到正确指向!
#4
to :BluntBlade(无锋之刃)
能讲解一下在
memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
中指针的转换过程吗?
主要是不明白 ‘类对象’, ‘函数’,‘普通数据’ 的‘指针’ 有何区别, 为何要通过long类型变量作为中间量,才能取得虚函数表中真正的虚函数的地址?
to inline(虚函数):
为何说 memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址?
新的地址 指的是怎么样的地址呢?
能讲解一下在
memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
中指针的转换过程吗?
主要是不明白 ‘类对象’, ‘函数’,‘普通数据’ 的‘指针’ 有何区别, 为何要通过long类型变量作为中间量,才能取得虚函数表中真正的虚函数的地址?
to inline(虚函数):
为何说 memcpy(&p,b_ptr,sizeof(long)); 是将类对象的地址值解释为一个新的地址?
新的地址 指的是怎么样的地址呢?
#5
b_ptr base: A base's vbtl
vptr ptr to f()
+-------+ +-------+ +-------+
| &A | -> | &vbtl | -> | &f() |
+-------+ +-------+ +-------+
&表示内存空间中的值为某个对象的地址
memcpy的函数原型是memcpy(void *,void *,int)
先看memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy得到了目标地址(tbl_ptr的地址,long *类型,可以隐式转换为void *)
和源地址(b_ptr的值,即A的地址,base *类型,可以隐式转换为void *)。
因此得以调用memcpy。
执行memcpy后tlb_ptr开始的四字节内存中储存了&A开始的四字节内存原来的值,
即对象A的vptr的值,也就是vbtl的地址(因为base中没有定义数据成员,而定义
有虚函数,所以A中只有vptr)。
再看memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
memcpy得到了目标地址(p的地址, void (*)(base *),可以转换为void *)和源
地址(tbl_ptr的值,long类型,不能隐式转换为void *),故而不能调用memcpy。
于是必须通过reinterpret_cast<void *>(tlb_ptr)将tlb_ptr的值的类型long转化
为void *,这样就满足了memcpy的调用要求。
由上面的分析也可以得到:
tlb_ptr的值是对象里的vptr的值,即vtbl的地址
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
vptr ptr to f()
+-------+ +-------+ +-------+
| &A | -> | &vbtl | -> | &f() |
+-------+ +-------+ +-------+
&表示内存空间中的值为某个对象的地址
memcpy的函数原型是memcpy(void *,void *,int)
先看memcpy(&tlb_ptr,b_ptr,sizeof(long));
memcpy得到了目标地址(tbl_ptr的地址,long *类型,可以隐式转换为void *)
和源地址(b_ptr的值,即A的地址,base *类型,可以隐式转换为void *)。
因此得以调用memcpy。
执行memcpy后tlb_ptr开始的四字节内存中储存了&A开始的四字节内存原来的值,
即对象A的vptr的值,也就是vbtl的地址(因为base中没有定义数据成员,而定义
有虚函数,所以A中只有vptr)。
再看memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
memcpy得到了目标地址(p的地址, void (*)(base *),可以转换为void *)和源
地址(tbl_ptr的值,long类型,不能隐式转换为void *),故而不能调用memcpy。
于是必须通过reinterpret_cast<void *>(tlb_ptr)将tlb_ptr的值的类型long转化
为void *,这样就满足了memcpy的调用要求。
由上面的分析也可以得到:
tlb_ptr的值是对象里的vptr的值,即vtbl的地址
b_ptr的值是对象的地址
p的值是虚拟函数的真实地址
#6
inline兄的说法是:
b_ptr的值是对象的地址,*b_ptr的值就是vptr的值,即vbtl的地址。
memcpy(&p,b_ptr,sizeof(long));
可以通过编译,因为base *可以隐式地转换为void *。
这样p中的值就是vptr的值,即vbtl的地址,而不是函数的地址了。
单继承已经足够复杂,如果是多继承就更头大了。
如果楼主有心情的话可以去看《Inside The C++ Object Model》,里面有详尽的说明。
请指教。
b_ptr的值是对象的地址,*b_ptr的值就是vptr的值,即vbtl的地址。
memcpy(&p,b_ptr,sizeof(long));
可以通过编译,因为base *可以隐式地转换为void *。
这样p中的值就是vptr的值,即vbtl的地址,而不是函数的地址了。
单继承已经足够复杂,如果是多继承就更头大了。
如果楼主有心情的话可以去看《Inside The C++ Object Model》,里面有详尽的说明。
请指教。
#7
非常感谢 BluntBlade(无锋之刃) 。
现在终于明白原来传给 tlb_ptr 的是虚函数表的头地址,而不是那实际虚函数的地址。
看来我对指针的理解还不够深啊!
现在终于明白原来传给 tlb_ptr 的是虚函数表的头地址,而不是那实际虚函数的地址。
看来我对指针的理解还不够深啊!
#8
让我来解释一下你的行为吧:
memcpy(&tlb_ptr,b_ptr,sizeof(long));
将基类指针 b_ptr 所指对象 4 个单元内容复制到 long 型对象 tlb_ptr 中,
现在的 tlb_ptr 与 b_ptr 所指的对象其实完全相同,不过 tlb_ptr 需要重新解释
才能当作类对象使用,否则使用 tlb_ptr 只能当一个值来用!
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
reinterpret_cast<void *>(tlb_ptr) 将 tlb_ptr 的 值解释成地址!
乍一看好象这个地址是由类对象的位域"凑"成的!怎么能赋值给 p 呢?
但实际上并没有错,因为你的类中有虚函数,类中有一个"虚函数表",
这个虚函数表是一个指针数组,而 p 得到的是这个指针数组的首址!
总之,没有特别需要不要使用 reinterpret_cast
即类中声明的第一个虚函数的地址,所以下面的调用才能进行!
假设类中没有虚函数,那么无论你怎么重新解释后面的调用都将是没有定义的!!!
memcpy(&tlb_ptr,b_ptr,sizeof(long));
将基类指针 b_ptr 所指对象 4 个单元内容复制到 long 型对象 tlb_ptr 中,
现在的 tlb_ptr 与 b_ptr 所指的对象其实完全相同,不过 tlb_ptr 需要重新解释
才能当作类对象使用,否则使用 tlb_ptr 只能当一个值来用!
memcpy(&p,reinterpret_cast<void *>(tlb_ptr),sizeof(long));
reinterpret_cast<void *>(tlb_ptr) 将 tlb_ptr 的 值解释成地址!
乍一看好象这个地址是由类对象的位域"凑"成的!怎么能赋值给 p 呢?
但实际上并没有错,因为你的类中有虚函数,类中有一个"虚函数表",
这个虚函数表是一个指针数组,而 p 得到的是这个指针数组的首址!
总之,没有特别需要不要使用 reinterpret_cast
即类中声明的第一个虚函数的地址,所以下面的调用才能进行!
假设类中没有虚函数,那么无论你怎么重新解释后面的调用都将是没有定义的!!!
#9
指针这玩意儿,够呛。楼主好好研究一下咯。
不过老实说,指针就是C/C++乃至计算机的灵魂级事物。
不过老实说,指针就是C/C++乃至计算机的灵魂级事物。
#10
谢谢各位老大。
不过,小弟还想问一个问题,就是我将我所给出的原码修改了一下,就是在base和inher1类中
加入其它的成员(私有,公有)数据与函数,在vc6.0下发觉程序依然正常运行,而且结果不变。这是否意味着某个类若有虚函数表,则表的头地址必定在类对象指针所指的内存地址
上(即“最开头”)???
不过,小弟还想问一个问题,就是我将我所给出的原码修改了一下,就是在base和inher1类中
加入其它的成员(私有,公有)数据与函数,在vc6.0下发觉程序依然正常运行,而且结果不变。这是否意味着某个类若有虚函数表,则表的头地址必定在类对象指针所指的内存地址
上(即“最开头”)???
#11
若某个类中有虚函数则会生成一个函数指针数组即虚函数表!
且在这个类对象的开头生成一个指针指向这个虚函数表的首址!
这些虚函数名被解释成为虚函数表的下标!
转换顺序先为从基类继承的虚函数,倘若本类中重新定义了则
本类中的覆盖从基类继承的!然后是在本类中顺序声明的虚函数!
但这个虚函数表不一定在类对象的体内!
你加一些非虚函数成员不影响结果!你在基类和派生类中加一些
虚函数试试!然后改变一下声明的顺序试试!再试一下派生类中
不重新定义基类的虚函数!你会明白的!
且在这个类对象的开头生成一个指针指向这个虚函数表的首址!
这些虚函数名被解释成为虚函数表的下标!
转换顺序先为从基类继承的虚函数,倘若本类中重新定义了则
本类中的覆盖从基类继承的!然后是在本类中顺序声明的虚函数!
但这个虚函数表不一定在类对象的体内!
你加一些非虚函数成员不影响结果!你在基类和派生类中加一些
虚函数试试!然后改变一下声明的顺序试试!再试一下派生类中
不重新定义基类的虚函数!你会明白的!
#12
vptr的位置是由编译器厂商决定的。不同的编译器有不同的现实方法,当然效率也不同。
#13
多谢各位大侠!