| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 763 人关注过本帖
标题:关于动态联编!
只看楼主 加入收藏
adaliuliu
Rank: 1
等 级:新手上路
帖 子:38
专家分:0
注 册:2006-8-21
收藏
 问题点数:0 回复次数:8 
关于动态联编!

#include"iostream.h"
class A
{
public:
void virtual print(){cout<<"a";}
};
class B : public A
{
void print(){cout<<"b";}
};

void main()
{
A a;
B b;
A*P=&b;
p->print();
}
根据动态联编,输出结果是:b,
我的问题是:编译器编译到A*P=&b时都做了些什么,如果已经给P指定了对象b,那为什么不能在编译期确定调用函数实体?print()明明就是的对象b的函数嘛。。请高手指点

搜索更多相关主题的帖子: 动态 print void public cout 
2006-09-09 18:35
眼底星空
Rank: 4
等 级:业余侠客
威 望:3
帖 子:85
专家分:289
注 册:2006-9-2
收藏
得分:0 
想了一下,好像我也解释不清,似懂非懂

等高手的解释。。

无为而为 && 每天进步一小点...
2006-09-09 19:49
song4
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:38
帖 子:1533
专家分:4
注 册:2006-3-25
收藏
得分:0 

因为P是基类的指针
对于子类和基类都可以指
正因为是虚函数,所以要等到运行到这里
才能知道到底运行子类基类的哪个函数
可以这么认为,真正传的是谁
谁的函数就给其他的覆盖

[此贴子已经被作者于2006-9-10 8:48:17编辑过]


嵌入式 ARM 单片机 驱动 RT操作系统 J2ME LINUX  Symbian C C++ 数据结构 JAVA Oracle 设计模式 软件工程 JSP
2006-09-10 08:27
眼底星空
Rank: 4
等 级:业余侠客
威 望:3
帖 子:85
专家分:289
注 册:2006-9-2
收藏
得分:0 
以下是引用song4在2006-9-10 8:27:43的发言:

因为P是基类的指针
对于子类和基类都可以指
正因为是虚函数,所以要等到运行到这里
才能知道到底运行子类基类的哪个函数
可以这么认为,真正传的是谁
谁的函数就给其他的覆盖

你的意思是说 编译器编译时对虚函数不会去判断指向的是谁,而只考虑它是哪个类的指针?按它的指针类型找相应的函数地址?运行时判断了指向的对象再用正确的函数地址覆盖?

但是关键是为什么不去判断指针指向的对象呢?明明可以判断的啊,是不是编译器刻意这样去处理虚函数的?

[此贴子已经被作者于2006-9-10 10:26:34编辑过]


无为而为 && 每天进步一小点...
2006-09-10 10:26
wfpb
Rank: 6Rank: 6
等 级:贵宾
威 望:29
帖 子:2188
专家分:0
注 册:2006-4-2
收藏
得分:0 

运行时访问内存地址。

那么要反跟函数时也是这样,需要知道函数的地址,如果是普通函数,继承而来,明确的知道函数的地址,但是虚拟函数则不同,因为基类个子类都有一个虚拟指针,这个指针则指向一个虚表,基类的虚表里面是基类所有的虚拟函数地址,子类的虚表里面就是继承自基类而没有在子类改写的函数的地址和改写基类以后的子类的虚拟函数的地址。

调用虚拟函数时,根据判断指针指向的内存对象来判断是用哪一个虚拟指针,访问哪一个虚表中的虚函数地址。(因此编译时无法确定调用哪一个地址上的虚拟函数)

如果是对象,那么已经明确确定了本身的类型,这是在编译时就已经确定了的,所以不需要运行时再来动态判断。

[此贴子已经被作者于2006-9-11 8:45:22编辑过]


[glow=255,red,2]wfpb的部落格[/glow] 学习成为生活的重要组成部分!
2006-09-10 12:01
眼底星空
Rank: 4
等 级:业余侠客
威 望:3
帖 子:85
专家分:289
注 册:2006-9-2
收藏
得分:0 
以下是引用wfpb在2006-9-10 12:01:12的发言:

运行时访问内存地址。

那么要反跟函数时也是这样,需要知道函数的地址,如果是普通函数,继承而来,明确的知道函数的地址,但是虚拟函数则不同,因为基类个子类都有一个虚拟指针,这个指针则指向一个虚表,基类的虚表里面是基类所有的虚拟函数地址,子类的虚表里面就是继承自基类而没有在子类改写的函数的地址和改写基类以后的子类的虚拟函数的地址。

调用虚拟函数时,根据判断指针指向的内存对象来判断是用哪一个虚拟指针,访问哪一个虚表中的虚函数地址。(因此编译时无法确定调用哪一个地址上的虚拟函数)

而如果是普通函数,不可能在子类重定义基类的函数,那属于重定义。编译应该会出错(如果这个地方我说错了,我会发现我有很大一个误区,所以请尽管指教)

如果是对象,那么已经明确确定了本身的类型,这是在编译时就已经确定了的,所以不需要运行时再来动态判断。



谢谢song4和wfpb 了。
今天又回头看了下深入浅出,讲的其实很透彻了,画的所占内存空间的图很清晰直观,才又有所理解
两位所说的也明白了
wfpb说的很详细清楚了,运行时才去访问p所指向的b,判断是B的对象所以去访问B的虚指针。
song4所说的覆盖是说B继承A的虚指针时因为B有改写print,所以这个虚指针原来指的A::print的地址将会被改为B::print的地址。

再谢谢两位斑竹的指点

ps:看来书得多啃几遍,才会越啃越出味







无为而为 && 每天进步一小点...
2006-09-10 17:14
song4
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:38
帖 子:1533
专家分:4
注 册:2006-3-25
收藏
得分:0 
以下是引用wfpb在2006-9-10 12:01:12的发言:

运行时访问内存地址。

那么要反跟函数时也是这样,需要知道函数的地址,如果是普通函数,继承而来,明确的知道函数的地址,但是虚拟函数则不同,因为基类个子类都有一个虚拟指针,这个指针则指向一个虚表,基类的虚表里面是基类所有的虚拟函数地址,子类的虚表里面就是继承自基类而没有在子类改写的函数的地址和改写基类以后的子类的虚拟函数的地址。

调用虚拟函数时,根据判断指针指向的内存对象来判断是用哪一个虚拟指针,访问哪一个虚表中的虚函数地址。(因此编译时无法确定调用哪一个地址上的虚拟函数)

而如果是普通函数,不可能在子类重定义基类的函数,那属于重定义。编译应该会出错(如果这个地方我说错了,我会发现我有很大一个误区,所以请尽管指教)

如果是对象,那么已经明确确定了本身的类型,这是在编译时就已经确定了的,所以不需要运行时再来动态判断。


什么是你说的普通函数?是类里的么

可以吧,那样就是名副其实的覆盖了


嵌入式 ARM 单片机 驱动 RT操作系统 J2ME LINUX  Symbian C C++ 数据结构 JAVA Oracle 设计模式 软件工程 JSP
2006-09-11 06:51
wfpb
Rank: 6Rank: 6
等 级:贵宾
威 望:29
帖 子:2188
专家分:0
注 册:2006-4-2
收藏
得分:0 

呵呵,果然这里说错了,哎,以后应该实验一次再说话的。

这个地方我是觉得说的有点奇怪,书上明明说过什么覆盖,呵呵,一时涂口快,请见量,我把上面那句删除了,以免误导后人。


[glow=255,red,2]wfpb的部落格[/glow] 学习成为生活的重要组成部分!
2006-09-11 08:44
song4
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:38
帖 子:1533
专家分:4
注 册:2006-3-25
收藏
得分:0 
呵呵 很正常
有事我也总凭知觉说话
但后来发现那里忘记了

嵌入式 ARM 单片机 驱动 RT操作系统 J2ME LINUX  Symbian C C++ 数据结构 JAVA Oracle 设计模式 软件工程 JSP
2006-09-11 09:10
快速回复:关于动态联编!
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.022806 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved