多态实现的原理------新标准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{ ...
随机推荐
- 1110 Complete Binary Tree
1110 Complete Binary Tree (25)(25 分) Given a tree, you are supposed to tell if it is a complete bina ...
- tomcat启动时的java_home和jre_home错误
The JRE_HOME environment variable is not defined correctlyThis environment variable is needed to run ...
- python并发之multiprocessing
由于GIL(全局解释锁)的问题,python多线程并不能充分利用多核处理器.如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.multiprocessing可以给每个进程赋 ...
- openstack 租户ip 手动配置 openstack静态租户ip
作者:[吴业亮]云计算开发工程师 博客:http://blog.csdn.net/wylfengyujiancheng 1.综述: 在日常开发和生产环境中经常需要将OpenStack虚拟机配置一个静态 ...
- 让你的javascript代码高大上
1,创造简短的写法 你可以这么写: var slice = Array.prototype.slice; slice.call(arguments); //转化成数组 也可以这么写:(ie9+) va ...
- 使用ResultSet结果集查询数据
直接上下代码: package com.learn.jdbc.chap05; import java.sql.Connection; import java.sql.PreparedStatement ...
- krpano之鼠标样式修改
引入cursors.js. cursors.js代码: <krpano > <events onxmlcomplete="action(qtvrcursor);" ...
- saltstack系列(三)——zmq订阅/发布模式
zmq订阅发布模式 server端代码: #coding=utf-8 ''''' 服务端,发布模式 ''' import zmq from random import randrange contex ...
- eclipse egit(分支管理 上)
这一章比较重要,讲述了Git比svn强大的地方,直接转载廖雪峰老师的文字,更好的理解 什么是分支 和 为什么分支git比svn做的更好 分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才 ...
- 【bzoj3942】[Usaco2015 Feb]Censoring
[题目大意] 有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程. [样例输入] whatth ...