面向对象的多态性(C++)
以C++为例三大特效:封装、继承、多态,面向对象的编程语言都具有这些特性。
那么本节来谈谈多态性,尽量说的简单些容易理解!
多态什么意思?即运行时多态,以相同的方式处理不同类型的对象,产生不同的结果!
请说人话,官方定义看不懂。。。。。。
多态即多种形态
1.普通函数的处理
首先需要知道在C++中对于类的处理,也是相当于把类转化为C的结构体的处理。让我们先来了解C++对于成员函数的处理的了解后才能了解虚函数最终到多态。
先来看看这段代码,
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:
int sex;
int age;
char name[32];
public:
void test()
{
cout << "Person::test()" << endl;
}
};
int main()
{
Person p;
p.test();
system("pause");
return 0;
}
运行中可以看到变量p的结构,但看不到类中的test()函数,其实编译后最终会把类里面的函数转化为普通的函数,因此类的实例对象的结构里并不包含成员函数。
编译程序后test()函数地址是确定的。
当我们调用p.test()函数时,我们来看看如何找到类的成员函数的。首先转到汇编代码查看:
1)首先将变量p地址保存到ecx 也就是我们经常看到书中所提到的this指针(this:指向对象自己的地址)
2)在test()函数内部中如果需要访问类实例对象的成员,那么只需要取得这个this指针就可以找到实例对象的成员变量了
对于一个实例对象可以包含成员函数和成员变量,例如javascript中是可以这样编写的,为了节省空间和代码重用性,c++处理函数最终会变为C中普通函数的处理,对于类中成员函数使用类成员变量的最终会转换为this去访问,编译后那么这个函数地址可以确定,调用时直接call 地址就可以了
2.虚函数的处理
//#include <cstdlib>
//#include <cstdio>
//#include <cstring>
#include <string>
#include <iostream>
using namespace std;
class Base {
public:
int age;
int sex;
virtual void fun(){
cout << "Person::fun()" << endl;
}
virtual void XXX() {
cout << "Person::XXX()" << endl;
}
public:
virtual void getName() {
cout << "Person::getName()" << endl;
}
};
class SubA : public Base
{
public:
int X;
virtual void fun() {
cout << "SubA::fun()" << endl;
XXX();
}
virtual void getName() {
cout << "SubA::getName()" << endl;
}
};
class SubB : public Base
{
public:
int Y;
virtual void fun() {
cout << "SubB::fun()" << endl;
}
virtual void getName() {
cout << "SubB::getName()" << endl;
}
};
int main()
{
Base base;
SubA subA;
SubB subB;
Base* pbase = &subA;
pbase->fun();
pbase->XXX();
pbase = &subB;
pbase->fun();
pbase->XXX();
system("pause");
return 0;
}
1)C++程序编译后每个类中的函数地址都是确定的,如上面代码中运行到SubA subA;位置时,那么系统就会为它开辟空间【其实本质就是调用构造函数、赋值存储等操作,栈空间本身就已经开辟好了的,如果是堆申请就是动态分配内存了,只是我们把某一块内存区域当做变量,对于栈空间上分配就是栈指针上下移动就可以得到获得地址位置】,这里没有写构造函数,但编译器会为我们自动添加一个无参的构造函数,所以不分析,如下图
2)当我们定义并分配内存的一个类对象变量时,对于类中有虚函数时(因为代码完成编译后就可以确定哪些类中有哪些虚函数,即每个类的虚函数表函数指针数组是确定的),实例化对象时调用构造函数时将在this首位置加入这个确定的虚函数的函数指针的数组的指针变量
3.多态的实现
如上面代码中
pbase指向不同的实例对象时,相同的代码pbase->fun()执行不同的代码就是产生了多态,也是说执行时多态!
总结:
1)程序编译后虚函数表指针的函数指针数组是已经确定的;
2)在定义变量申请内存时调用构造函数,构造函数中进行虚函数表指针的赋值;
3)在实例对象调用虚函数时,是进行数组的偏移得到实际函数地址进行调用的,因为数组的偏移位置是可以确定的;
4)每个实例对象中保存了虚函数指针地址,地址指向虚函数的函数指针的数组;
5)当基类指针指向不同派生类对象时,就能根据虚函数表数组位置索引得到实际地址(因此程序编译后就可以确定偏移位置),该地址指向派生类的重写函数;
6)虚函数表位置是根据写代码的顺序来安排的,即可确定数组所有索引位置该存放什么虚函数的地址;
不同派生类对象内的虚函数表数组的指针指向的虚函数表数组位置都相同,简单来说重写的就覆盖基类的,其实每个类只要有virtual关键字,就有虚函数表数组(内部存储函数指针)
程序在运行时,当我们在代码中改变给父类指针指向任意派生类对象时(pParent = pSubObj),那么随着派生对象的改变调用虚函数(pParent->fun()),就会调用实际对象中的虚函数,这就是运行时多态的体现!
本质:
1) pbase指向的是派生对象的首地址,那么由于当建立新对象时,这个对象在内存中只存储(__vpfn+类中成员变量),首地址即是虚函数指针的地址,根据这个地址进行偏移[+0,+4,+8,+12 ......]来找到派生类的虚函数
简单C模仿虚函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CLASS struct
CLASS Parent
{
void** __vpfn;
int age;
int sex;
};
// 虚函数
void fun1Parent(int x, CLASS Parent* This)
{
printf("fun1Parent %d--%p\r\n", x, This);
}
// 普通函数
void fun2Parent(CLASS Parent* This)
{
}
// 虚函数
void fun3Parent(CLASS Parent* This)
{
printf("fun3Parent---%p\r\n", This);
}
void* Parent_vpfn[] = {
fun1Parent,
fun3Parent
};
int main()
{
//Parent p;
CLASS Parent p = {Parent_vpfn};
//p.fun1Parent(100);
((void (*)(int x, CLASS Parent* This))(*(p.__vpfn)))(100,&p);
//p.fun3Parent();
//((int*)(*(p.__vpfn))+1)
system("pause");
return 0;
}
面向对象的多态性(C++)的更多相关文章
- JAVA面向对象的多态性
什么是多态?简而言之就是相同的行为,不同的实现. 而多态也分为静态多态(重载).动态多态(重写)和动态绑定. 静态动态,实际就是指的重载的概念,是系统在编译时,就能知晓该具体调用哪个方法.动态多态指在 ...
- Java面向对象_多态性、instanceof关键字
一.多态 分类:方法的重载与重写:对象的多态性 对象的多态性:向上转型:将子类实例转为父类实例 格式:父类 父类对象=子类实例;是自动转换 向下转型:将父类实例转为子类实例 格式:子类 子类对 ...
- python 之 面向对象(多态性、装饰器方法 内置函数补充)
7.6 多态性 1 什么是多态性 多态指的是同一种事物多种形态,在程序中用继承可以表现出多态.多态性:可以在不用考虑对象具体类型的前提下而直接使用对象下的方法 2.为什要用多态 用基类创建一套统一的规 ...
- 《Java编程思想》之重点笔记——多态性理解
Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意味着通常情况下,我们不必判定是否应该进行动态绑定— ...
- python运维开发(八)----面向对象(下)
内容目录: 面向对象三大特性之多态性 面向对象中的成员:字段.方法.属性 类的成员修饰符 类的特殊成员 特殊成员方法 面向对象其他 异常处理 设计模式之单例模式 面向对象的多态性 多态性:即指多种形态 ...
- java面向对象总结(一)
1. 对象的概念及面向对象的三个基本特征 面向对象的三大核心特性 面向对象开发模式更有利于人们开拓思维,在具体的开发过程中便于程序的划分,方便程序员分工合作,提高开发效率.面向对象程序设计有以下优点. ...
- Java面向对象----个人参考资料
Java面向对象 :什么是面向对象.类与对象.封装.构造方法.static关键字.继承.抽象类.接口.多态 一.什么是面向对象 1.面向过程思想 面向过程:(PO,Procedure Oriented ...
- java第四节 类的继承/抽象/接口/多态性
/* 类的继承 类的继承可以简化类的定义 java只支持单继承,不允许多重继承 可以有多层继承,即一个类可以继承其一个类的子类,如类B继承了类A,类C又可以继承类B 那么类C也间接继承了类A 子类继承 ...
- C++中多态性学习(上)
多态性学习(上) 什么是多态? 多态是指同样的消息被不同类型的对象接收时导致不同的行为.所谓消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数.虽然这看上去好像很高级的样子 ...
随机推荐
- pgadmin4 python
安装安装包 # sudo apt-get install build-essential libssl-dev libffi-dev libgmp3-dev virtualenv python-pip ...
- iOS-方法之+ initialize 与 +load
Objective-C 有两个神奇的方法:+load 和 +initialize,这两个方法在类被使用时会自动调用.但是两个方法的不同点会导致应用层面上性能的显著差异. 一.+ initialize ...
- docker管理工具protainer
ortainer(基于 Go) 是一个轻量级的Web管理界面,可让您轻松管理 Docker 主机 或 Swarm 集群.
- swift中 ?和 !的区别
可选类型(?)与强制解析运算符(!) ?是一种判断后再拆包的语法糖 !是一种强制拆包的语法糖 当你不确定有值的时候就可以用 ? 当你确定有值的时候可以用 ! ?的几种使用场景:1. ...
- swift 桥接 Bridging 的创建和使用
swift编程时,大概率会用到OC的文件,这时就要使用swift与oc的桥接文件.桥接文件以 XXXX-Bridging-header.h 这样子的文件名形式为标准,XXXX是你的项目名字. 具体 ...
- fio 测试 磁盘I/O: ls -1 /sys/block/sda/queue/ | awk '{cmd="cat "i$0; print i$0; system(cmd) }' i=`pwd`'/'
小型计算机系统接口(SCSI,Small Computer System Interface) SAS(Serial Attached SCSI,串列SCSI) SCSI 硬盘名称: sd[a-p] ...
- CentOS安装Yarn只需两步就搞定
Yarn 是一个依赖管理工具.它能够管理你的代码,并与全世界的开发者分享代码.Yarn 是高效.安全和可靠的,你完全可以安心使用.代码是通过包(有时也被称为组件). 在每一个包中会定义一个 packa ...
- Laravel展示产品-CRUD之show
上一篇讲了Laravel创建产品-CRUD之Create and Store,现在我们来做产品展示模块,用到是show,①首先我们先修改controller,文件是在/app/Http/Control ...
- 如何用python发邮件
python发送各类邮件的主要方法 一.相关模块介绍 发送邮件主要用到了smtplib和email两个模块,这里首先就两个模块进行一下简单的介绍: 1.smtplib模块 smtplib.SM ...
- GENIL_BOL_BROWSER, GENIL_MODEL_BROWSER,BSP_WD_CMPWB 使用方法
一:GENIL_BOL_BROWSER 使用方法 1: 进入x3c系统.输入T-CODE GENIL_BOL_BROWSER 2: 输入一个component set 名称 3: 选择一个对象,双 ...