面向对象的多态性(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++中多态性学习(上)
多态性学习(上) 什么是多态? 多态是指同样的消息被不同类型的对象接收时导致不同的行为.所谓消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数.虽然这看上去好像很高级的样子 ...
随机推荐
- HBase 笔记
参考资料:HBase权威指南 一行由若干列组成,若干列又构成一个列族一个列族的所有列存储在同一个底层的存储文件里,这个文件叫HFile列族的数量有限制:一个列族里列的数量没限制谓词删除:例如允许用户只 ...
- Python开发【笔记】:从海量文件的目录中获取文件名--方法性能对比
Python获取文件名的方法性能对比 前言:平常在python中从文件夹中获取文件名的简单方法 os.system('ll /data/') 但是当文件夹中含有巨量文件时,这种方式完全是行不通 ...
- java 流程控制--猜数字
import java.util.Scanner; import java.util.Random; public class GuessNum{ public static void main(St ...
- swift 颜色设置方法
如下: func RGB(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor { return UIColor.init(red: r ...
- js正则表达式:学习网址和部分正则验证
https://www.cnblogs.com/chenmeng0818/p/6370819.html ① 不以0开头的多个数字,但可以是单个0,必须为数字,位数不允许超过10个. var reg=/ ...
- python numpy初始化一个图像矩阵
mask_all = np.zeros((256, 256), dtype='uint8') 单通道 mask_all_enlarge = np.zeros((256, 256, 3), dtype ...
- springmvc实现文件上传
springmvc实现文件上传 多数文件上传都是通过表单形式提交给后台服务器的,因此,要实现文件上传功能,就需要提供一个文件上传的表单,而该表单就要满足以下3个条件 (1)form表彰的method属 ...
- Windows 7中200M神秘隐藏分区
裸机全新安装Windows 7的用户,在安装完成后运行diskmgmt.msc打开磁盘管理器,可以看到在系统分区(一般为C分区)之前有一个大小为200MB的隐藏分区.这个特殊的隐藏分区与Windows ...
- NYOJ 方案数量
1.递归求解(直接递归会超时,要用备忘录法) # include<iostream> # include<stdio.h> #include <map> using ...
- 向数据库中添加数据,通过se16 不能添加,通过 代码可以添加的原因
1: 在向数据库中添加数据时,通过客户端se16 准备对 数据表进行添加数据,提示如下: 找了以下原因,如下: https://www.baidu.com/link?url=3yRtAfY1_9XG ...