=========================定义=========================

将一个调用函数连接上正确的被调用函数,这个过程就叫做函数的联编,简称联编。在C++中,一共有两种联编的方式:

  1. 静态联编
    #define:静态联编是指联编工作出现在编译连接阶段
    #特点:① 静态联编就是指被调用函数执行调用函数之间的关系以及它们在内存中的地址在编译的时候已经确定好了,运行会发生变化
               ② 由于对象不用对自身进行跟踪,因此速度浪费比较小,但是灵活性较差。
  2. 动态联编
    #define:动态联编是指在程序运行的时候才进行的联编工作。
    #特点:① 由于编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序执行时才能确定将要调用的函数。要确切之道该调用的函数,就必须要求联编工作在程序运行时才能进行。
               ② 虽然可以追踪对象,灵活性较强,但是速度浪费严重。

=========================virtual的作用=========================

首先,我们来看几个例子:

view sourceprint?
01 //例1 用父类对象的指针指向子类对象
02 #include <iostream>
03 using namespace std;
04   
05 class father
06 {
07 public:
08     father(){};
09     father(int i){age=i;}
10     void print()const{cout<<"father's age is "<<age<<endl;}
11 protected: //protected声明的成员可以被派生类访问
12     int age;
13 };
14   
15 class son:public father
16 {
17 public:
18     son(){};
19     son(int j){age=j;}
20     void print()const{cout<<"son's age is "<<age<<endl;}
21 };
22   
23 int main()
24 {
25     cout<<"一般情况:\n";
26     father dad(56);
27     dad.print();
28     son boy(22);
29     boy.print();
30     cout<<"指针情况:\n";
31     father *liu=new father(56);
32     liu->print();
33     son *uniqueliu=new son(22);
34     //father *uniqueliu=new son(22);
35     uniqueliu->print();
36 }

我们先来看一下这个程序的运行结果:

上面这个结果很好理解吧,我就不解释了。但是如果我们这里把程序33行的注释去掉,把32行注释起来。那么程序运行的结果就会出现如下的情况:

这个就很奇怪了。我们的初衷是想用一个父类指针来指向一个子类对象。但是最后调用的print函数却是父类对象的。这个原因就是因为我们没有在父类中的print()前面加上关键字virtual的原因了。这是因为在函数print()函数前面加上了关键字virtual,就表示该函数是有多种形态的,说白了,就是这个print()函数可以被很多对象所拥有,而且各自实现的功能是不一样的,这样一来,就是先了多态。总结一下,我们只要在基类的成员函数前面加上virual,那么就算派生类的对象重新实现了同名函数,编译器就会自动判断是哪个对象调用了它,然后用该对象的同名函数,而不会采用基类的函数了。程序如下所示:

view sourceprint?
01 //例2 用父类对象的指针指向子类对象+virtual
02 #include <iostream>
03 using namespace std;
04   
05 class father
06 {
07 public:
08     father(){};
09     father(int i){age=i;}
10     virtual void print()const{cout<<"father's age is "<<age<<endl;}
11 protected: //protected声明的成员可以被派生类访问
12     int age;
13 };
14   
15 class son:public father
16 {
17 public:
18     son(){};
19     son(int j){age=j;}
20     void print()const{cout<<"son's age is "<<age<<endl;}
21 };
22   
23 int main()
24 {
25     cout<<"一般情况:\n";
26     father dad(56);
27     dad.print();
28     son boy(22);
29     boy.print();
30     cout<<"指针情况:\n";
31     father *liu=new father(56);
32     liu->print();
33     //son *uniqueliu=new son(22);
34     father *uniqueliu=new son(22);
35     uniqueliu->print();
36 }

这样,我们在程序的第10行上加上了virtual关键字。它就将基类的print()函数声明成了虚函数。这样一来,我们就可以调用子类的print()函数了,上图发真相!

=========================virtual与联编之间的关系=========================

相对于例1来讲,在这里面调用的关系就是静态联编,因为在程序的编译阶段就已经把对象和它们所指向的函数紧紧地联系在一起了,运行的时候自然就不会有任何改变。而对于例2来讲,由于在父类的[rint()函数前面加上了关键字virtual,那么这个时候父类中的print()函数就变成了虚函数,虚函数就可以实现运行时的动态联编。这是因为virtual会让编译器自动地区寻找与之对应的对象,所以输出才会于红框所示,“son's age is 22”。

特别注意一点!!!只有在使用指针或者是引用的时候,才能够实现动态联编。

回复 引用 查看   

#1楼2011-07-22 22:51 | hailong

解释的很好啊,O(∩_∩)O~,你说的最后,必须是在指针或引用的时候才会发生,然后我想如果直接是调用复制构造函数的时候,是不是会发生,结果进行了修改,看了下结果:

view sourceprint?
01 //例1 用父类对象的指针指向子类对象
02 #include <iostream>
03 using namespace std;
04 class son;
05 class father
06 {
07 public:
08     father(){};
09     father(int i){age=i;}
10     father(father &f):age(f.age){}
11     father(son &s){}
12     virtual void print()const{cout<<"father's age is "<<age<<endl;}
13 protected: //protected声明的成员可以被派生类访问
14     int age;
15 };
16   
17 class son:public father
18 {
19 public:
20     son(){};
21     son(int j){age=j;}
22     void print()const{cout<<"son's age is "<<age<<endl;}
23 };
24   
25 int main()
26 {
27     cout<<"一般情况:\n";
28     father dad(56);
29     dad.print();
30     son boy(22);
31     boy.print();
32     cout<<"指针情况:\n";
33     father *liu=new father(56);
34     liu->print();
35     //son *uniqueliu=new son(22);
36     father uniqueliu=boy;
37     uniqueliu.print();
38 }

而看了相关介绍,知道在指针或者引用对象时,会产生并调用虚函数表,当基类被派生类赋值时,调用成员函数时,如果基类的虚函数在派生类中存在则调用派生类,若无则调用基类的。
如:

view sourceprint?
01 //例1 用父类对象的指针指向子类对象
02 #include <iostream>
03 using namespace std;
04 class son;
05 class father
06 {
07 public:
08     father(){};
09     father(int i){age=i;}
10     father(father &f):age(f.age){}
11     father(son &s){}
12     virtual void print()const{cout<<"father's age is "<<age<<endl;}
13     virtual void is_print()const{cout << "father's print\n";}
14 protected: //protected声明的成员可以被派生类访问
15     int age;
16 };
17   
18 class son:public father
19 {
20 public:
21     son(){};
22     son(int j){age=j;}
23     void print()const{cout<<"son's age is "<<age<<endl;}
24 };
25   
26 int main()
27 {
28     cout<<"一般情况:\n";
29     father dad(56);
30     dad.print();
31     son boy(22);
32     boy.print();
33     cout<<"指针情况:\n";
34     father *liu=new father(56);
35     liu->print();
36     //son *uniqueliu=new son(22);
37     father &uniqueliu=boy;
38     uniqueliu.print();
39     uniqueliu.is_print();
40 }

回复 引用 查看   

#2楼[楼主]2011-07-22 23:13 | uniqueliu

@hailong
引用而看了相关介绍,知道在指针或者引用对象时,会产生并调用虚函数表,当基类被派生类赋值时,调用成员函数时,如果基类的虚函数在派生类中存在则调用派生类,若无则调用基类的
学习了~~~^_^
另外,hailong兄弟,我认为你最后这句话
引用uniqueliu.is_print();
是点睛之笔啊!你这样一写,就更加诠释了虚函数的作用和你上面那句话“调用虚函数表”的作用了。非常谢谢你给我的提醒,thank you very much!!!!我们以后一定得多交流哈,^_^

转载于:https://www.cnblogs.com/rowp/archive/2011/10/18/2216212.html

静态联编与动态联编之virtual的作用相关推荐

  1. 关于静态联编和动态联编

    首先什么是联编? 通俗的来说联编就是函数调用(操作调用)与执行该操作(函数)的代码段之间的映射关系. 按照联编所进行的阶段不同可分为静态联编和动态联编 举个列子 #include<iostrea ...

  2. Cpp 对象模型探索 / 静态联编和动态联编

    一.源码 #include <iostream>class Father { public:Father(){/*** 该处直接将该对象清零,意味着虚函数表指针亦被清零.*/memset( ...

  3. c++中的多态---1(多态概念,静态联编和动态联编,多态原理解析,重载,重写,重定义的对比)

    多态的基本概念 多态是面向对象设计语言数据抽象和继承之外的第三个基本特征 多态性(polymorphism)提供接口与具体实现之间的另一层隔膜,从而将"what"和"ho ...

  4. C++静态联编与动态联编

    目录 基本概念 举个栗子 基本概念 联编 就是将模块或者函数合并在一起生成可执行代码的处理过程(函数调用),按照联编所进行的阶段不同,可分为两种不同的联编方式:静态联编和动态联编. 函数调用和实现的过 ...

  5. C++的静态联编和动态联编

    联编的概念 联编是指一个计算机程序自身彼此关联的过程,在这个联编过程中,需要确定程序中的操作调用(函数调用)与执行该操作(函数)的代码段之间的映射关系. 意思就是这个函数的实现有多种,联编就是把调用和 ...

  6. 虚拟函数-1、静态联编与动态联编,引入虚函数

    在实际开发工作中,为提高代码的重用性,编写通用的功能模块,往往需要设计处理几种不同对象的通用程序,如示例2.1所示. 示例清单2.1 #include "stdio.h" #inc ...

  7. 静态联编,动态联编,类指针之间的关系,虚函数与多态性,纯虚函数,虚析构函数

    1.静态联编,是程序的匹配,连接在编译阶段实现,也称为早期匹配.重载函数使用静态联编. 2.动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编.switch语句和if语句是动态联编的例子. #i ...

  8. C++之静态联编与动态联编及virtual关键字的作用

    定义 将一个调用函数连接上正确的被调用函数,这个过程就叫做函数的联编,简称联编.在C++中,一共有两种联编的方式: 静态联编 #define:静态联编是指联编工作出现 在编译连接阶段. 特点:① 静态 ...

  9. 动态联编与静态联编的区别

    摘要]:本文阐述了静态联编和动态联编的概念和区别,通过具体实例分析了实现动态联编的条件,指出了虚函数是实现动态联编的基础. [关键词]:静态联编:动态联编:虚函数 在C++中,联编是指一个计算机程序的 ...

最新文章

  1. cacti监控(3)配置cacti
  2. 健康大脑结构的变化如何影响认知的?
  3. jQueryui autocomplete使用示例
  4. MATLAB图片的保存与导出
  5. word格式:导出与导入(如何将一套格式样式应用于另一个文档)- 教程篇
  6. 五大领域总目标指南_幼儿教师这样读《指南》事半功倍
  7. 小鹏汽车创始人何小鹏:做梦梦到投资人要投资
  8. Flash MX 2004 中的文本遮罩
  9. 大学生简单个人静态HTML网页设计作品 DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载 HTML5期末大作业 (1)
  10. 手机短信校验(前端js)
  11. 2011系列服务器,2011年中服务器领域大事件盘点
  12. fastposter 2.1.1 紧急版本发布 电商级海报生成器
  13. 计算幻术之路(二):增强现实的现实
  14. iOS16锁屏可定制,WPS回应“删除用户本地文件”,紫光集团重整阶段即将收官,今日更多大新闻在此...
  15. JS如何向对象中添加元素
  16. log_mode的模式介绍
  17. DIY剪刀石头布机器人(一)
  18. LSH(Locality Sensitive Hashing)基本思想
  19. 中国真空溅射镀膜机行业市场供需与战略研究报告
  20. 【C++学习笔记】看完这篇,C++ 的链接问题不怕你搞不明白!

热门文章

  1. Kafka不停机,如何无感知迁移ZooKeeper集群?
  2. 每周一个 Python 模块 | time
  3. 解读微软开源MMLSpark:统一的大规模机器学习生态系统
  4. 漫画:什么是冒泡排序?
  5. 调用dubbo接口出现多次
  6. 安装cactiez v11对windows和linux系统进行监控
  7. Jackson解析JavaBean空值不显示问题
  8. SQL2K数据库开发二十三之索引操作重建索引
  9. 【动态规划】装箱问题
  10. amazeui页面分析之登录页面