多态实现的原理------新标准c++程序设计
“多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定。例子:
#include<iostream>
using namespace std;
class A{
public:
int i;
virtual void func(){};
virtual void func2(){}; //如果为只有一个去掉 virtual 关键字即virtual void func2(){};变为 void func2(){}; 输出结果不变 仍为 8,12
}; //当 virtual 关键字都去掉时,结果才为 4,8
class B :public
int j;
void func(){}
};
int main(){
cout<<sizeof(A)<<","<<sizeof(B);
};
输出结果:
8,12
如果将程序中的 virtual 关键字去掉:
#include<iostream>
using namespace std;
class A{
public:
int i;
void func(){};
void func2(){};
};
class B :public A{
int j;
void func(){}
};
int main(){
cout<<sizeof(A)<<","<<sizeof(B);
};
输出结果:
4,8
对比发现,有了虚函数以后,对象占用的存储空间比没有虚函数时多了4个字节。实际上,任何有虚函数的类及其派生类的对象都包含这多出来的4个字节,这4个字节就是实现多态的关键——它位于对象存储空间的最前端,其中存放虚函数表的地址。
每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该表的任何对象中都放着该虚函数表的指针(可以认为这是由编译器自动添加到构造函数中的指令完成的)。虚函数表是编译器生成的,程序运行时被载入内存。一个类的虚函数表中列出了该类的全部虚函数地址。例如,在上面的程序中,类A对象的存储空间以及虚函数表(假定类A还有其他虚函数)如图
类B对象的存储空间以及虚函数表(假定类B还有其他虚函数)如图
多态的函数调用语句被编译成根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令。
假设pa的类型是A*,则pa->func()这条语句的执行过程如下:
(1)取出pa指针所指位置的前4个字节,即对象所属的类的虚函数表的地址(在64位计算机中,由于指针占8个字节,所以要取出8个字节)。如果pa指向的是类A的对象,则这个地址就是类A的虚函数表的地址;如果pa指向的是类B的对象,则这个地址就是类B的虚函数表的地址。
(2)根据虚函数表的地址找到虚函数表,在其中查找要调用的虚函数的地址。不妨认为虚函数表是以函数名作为索引来查找的,虽然还有更高效的查找方法。如果pa指向的是类A的对象,自然就会在类A的虚函数表中查找A::func的地址;如果pa指向的是类B的对象,就会在类B的虚函数表中查出B::func的地址。类B没有自己的func2函数,因此在类B的虚函数表中保存的是A::func2的地址,这样,即便pa指向类B的对象,"pa->func2();"这条语句在执行过程中也能在类B的虚函数表中找到A::func2的地址。
(3)根据找到的虚函数的地址调用虚函数。
由以上过程可以看出,只要是通过基类指针或基类引用调用虚函数的语句,就一定是多态的。也一定会执行上面的查表过程,哪怕这个虚函数仅在基类中有,在派生类中没有。
新标准c++程序设计
多态实现的原理------新标准c++程序设计的更多相关文章
- 在成员函数中调用虚函数(关于多态的注意事项)------新标准c++程序设计
类的成员函数之间可以互相调用.在成员函数(静态成员函数.构造函数和析构函数除外)中调用其他虚成员函数的语句是多态的.例如: #include<iostream> using namespa ...
- 多态的作用-游戏编程展示------新标准c++程序设计
游戏软件的开发最能体现面向对象设计方法的优势.游戏中的人物.道具.建筑物.场景等都是很直观的对象,游戏运行的过程就是这些对象相互作用的过程.每个对象都有自己的属性和方法,不同对象也可能有共同的属性和方 ...
- 正确处理类的复合关系------新标准c++程序设计
假设要编写一个小区养狗管理程序,该程序需要一个“主人”类,还需要一个“狗”类.狗是有主人的,主人也有狗.假定狗只有一个主人,但一个主人可以有最多10条狗.该如何处理“主人”类和“狗”类的关系呢?下面是 ...
- 类与类之间的两种关系------新标准c++程序设计
在c++中,类和类之间有两种基本关系:复合关系和继承关系. 复合关系也称为“has a”关系或“有”的关系,表现为封闭类,即一个类以另一个类的对象作为成员变量. 继承关系也称为“is a”关系或“是” ...
- 复制构造函数被调用的三种情况------新标准c++程序设计
1.当用一个对象去初始化同类的另一个对象时,会引发复制构造函数被调用.例如,下面的两条语句都会引发复制构造函数的调用,用以初始化c2. C c2 (c1); C c2=c1; 这两条语句是等价的.注意 ...
- 析构函数的调用------新标准c++程序设计
示例1: #include<iostream> using namespace std; class CDemo{ public: ~CDemo(){cout<<"d ...
- 类型转换构造函数 及使用explicit避免类型自动转换------新标准c++程序设计
类型转换构造函数: 除复制构造函数外,只有一个参数的构造函数一般可以称作类型转换构造函数,因为这样的构造函数能起到类型自动转换的作用.例如下面的程序: #include<iostream> ...
- this指针------新标准c++程序设计
背景: c++是在c语言的基础上发展而来的,第一个c++的编译器实际上是将c++程序翻译成c语言程序,然后再用c语言编译器进行编译.c语言没有类的概念,只有结构,函数都是全局函数,没有成员函数.翻 ...
- 类的互相包含------新标准c++程序设计
#include<iostream> using namespace std; class A; class B{ public: void f(A* pt){}; } class A{ ...
随机推荐
- angular 本地存储
localStorage四种方法: localStorage.getItem(key):获取指定key本地存储的值localStorage.setItem(key,value):将value存储到ke ...
- ejs的简单应用
获取数据 var init = function(){ api.my_bean_list({ },function(datas){ console.log(datas); if (datas.errn ...
- linux那点事儿(七)----文件系统管理
如果你是一位忠实的windows 用户,那么现在请你打开的的c盘,打开WINDWOS目录,下面存放了哪些文件和目录,相信没有人关心过吧!即便是用windows多年的人.额!其实,我也知道WINDOWS ...
- 不使用sudo命令执行docker
不使用sudo命令执行docker 2015-09-11 11:03:05 王春生 8049 最后编辑:王春生 于 2015-09-11 12:18:30 简介:本篇文章介绍如何不使用sudo命 ...
- 【开发工具】最强Git使用总结
目录 必读材料 常用小结 Git操作流程 Git常用操作命令 - 代码提交和同步代码 Git常用操作命令 - 代码撤销和撤销同步 Git常用操作命令 - 其它常用命令 Git是分布式代码托管的标杆,这 ...
- Mycat实战之连续分片
1 按照日期(天)分片: 从开始日期算起,按照天数来分片 例如,从2017-11-01,每10天一个分片且可以指定结束日期 注意事项:需要提前将分片规划好,建好,否则有可能日期超出实际配置分片数 1. ...
- axis调用webservice客户端开发
第一步:wsdl2Java.bat文件编写 Axis_Lib表示依赖的jar包路径 Output_Path表示生成的class路径 Package包名 还需要手动更改 -p %Package%表示we ...
- python:for语句的使用方法
for循环的语法格式: for i in range(n):#从数据类型中拿一个值赋值给i print(i)#打印i 例如: #for for i in range (1,6,2):#从一开始到六之前 ...
- 【原】Coursera—Andrew Ng机器学习—Week 3 习题—Logistic Regression 逻辑回归
课上习题 [1]线性回归 Answer: D A 特征缩放不起作用,B for all 不对,C zero error不对 [2]概率 Answer:A [3]预测图形 Answer:A 5 - x1 ...
- 【bzoj2186】[Sdoi2008]沙拉公主的困惑
2186: [Sdoi2008]沙拉公主的困惑 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 3303 Solved: 1129[Submit][S ...