C++之多态与虚函数

news2024/12/22 12:58:02

文章目录

    • 初识多态
    • 运行时多态的原理
    • 静态联编和动态联编

初识多态

  • 多态性是面向对象程序设计的关键技术之一。若程序不支持多态,不能称为面向对象的语言
  • 编译时多态:通过函数重载实现,早期绑定
  • 运行时多态:在程序执行过成中,动态的确定,它是通过类继承关系public和虚函数实现的,目的也是建立一种通用的程序。
  • 运行时多态的设计思想:
  • 对于相关的类型,确定他们之间的一些共同特征,(属性和方法),将共同特征被转移到基类中,然后在基类中,把这些共同的函数或方法声明为公有的虚函数接口,然后使用派生类继承基类,并且在派生类中重写这些虚函数,已完成具体的功能。

虚函数定义:

class Animal
{
private:
	string _name;
public:
	Animal(const string& name):_name(name){}
	~Animal(){}
	virtual void eat() { cout << "eat..." << endl; }
	virtual void walk() { cout << "walk..." << endl; }
	virtual void talk() { cout << "walk..." << endl; }
	void PrintInfo()const
	{
		cout << _name << endl;
	}
};

class Dog:public Animal
{

public:
	Dog(const string& name):Animal(name){}
	~Dog(){}
	void eat() { cout << "Dog eat bone" << endl; }
	void walk() { cout << "Dog walk Dog" << endl; }
	void talk() { cout << "Dong walk :wang wang" << endl; }
};

class Cat:public Animal
{
public:
	Cat(const string &name):Animal(name){}
	~Cat(){}
	void eat() { cout << "cat eat:fish" << endl; }
	void walk() { cout << "cat walk silent" << endl; }
	void talk() { cout << "Cat talk miao maio " << endl; }
};
void fun(Animal * ap) //打印的是狗
{
	if (ap == nullptr)return;
	ap->eat();
	ap->walk();
	ap->talk();
}
void funa(Animal& ma)  //打印的是猫
{
	ma.eat();
	ma.talk(); 
	ma.walk();
	ma.PrintInfo(); //不具有多态性
}
void func(Animal animal) //不具有多态,打印的是动物本身
{
	animal.eat();
	animal.talk();
	animal.walk();
	animal.PrintInfo();
}
int main()
{
	Animal* ap = nullptr;
	Cat cat("bsm");
	Dog dog("erha");
	fun(&dog);
	funa(cat);
	func(cat);
}

总结:运行时多态性,公有继承+虚函数+(指针或引用调用虚函数)

  • 静态联编和动态联编

函数的重写,函数参数,函数名,函数返回类型必须相同(函数重写是同名覆盖的一种特殊情况)

class Object
{
public:
	virtual void add(int x) { cout << "Object::add(int)" << endl; }
};
class Base:public Object
{
public:
	virtual void add(int a) { cout << "Base::add(int)" << endl; }
};

int main()
{
	Base base;
	Object* op = &base;
	Object& obj = base;
	op->add(10); //拿指针或者引用调用虚方法,是动态联编,要通过查虚表的方式
	obj.add(10);

	Object os;//静态联编
	os.add(20);
	os = base;
	os.add(20);
}

协变

class Object
{
public:
	virtual Object* Get() { return this; }
};
class Base :public Object
{
public:
	virtual Base* Get() { return this; }
};
int main()
{
	Base base;
	Object* op = &base;
	op->Get();
}
  • 只有类的成员函数才能说明虚函数,这是因为虚函数仅适用于有继承关系的类对象,友元函数和全局函数不能做为虚函数
  • 静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数
  • 构造函数和拷贝构造函数不能作为虚函数,构造函数和拷贝构造函数是设置虚表指针
  • 析构函数可定义为虚函数,构造函数不能定义虚函数,因为在调动构造函数时对象还没有完全实例化(虚表指针没有设置),在基类中及其派生类中都动态分配内存空间,必须把析构函数定义为虚函数,实现撤销对象时的多态性
  • 运行时的多态,函数执行速度要慢一些,为了实现多态性,每一个派生类中均要保存相应的虚函数的入口地址表,函数的调用机制也是间接实现,所以多态性总要付出一定的代价,但通用性是更高的目标
  • 如果定义放在类外,virtual只能加载函数的声明前面,不能加在函数的定义前面。正确的定义必须不包括virtual
    在这里插入图片描述

运行时多态的原理

  • 虚函数指针表简称为虚表,虚表就是虚函数指针的集合
  • 虚表本质是一个存储虚函数指针的指针数组
  • 这个数组的首元素之上存储RTTI(运行时类型识别信息的指针)
  • 从数组下标0开始一次存储虚函数地址,最后面放了一个nullptr
  • 虚表存储在只读数据段(.rodata)
  • 类型设计中定义了虚函数,此类型就有了对应的虚表
  • v代表virtual,f代表function,table代表表,数组
  • 使用此类型定义的对象就含有一个指向虚标的指针,名字是vfptr
  • vfptr存储在对象中

函数的重写:(同名覆盖)

using namespace std;

class Object
{
private:
	int value;
public:
	Object(int x=0):value(x){}
	virtual void add() { cout << "object::add()" << endl; }
	virtual void fun(){ cout << "object::fun()" << endl; }
	virtual void print(){ cout << "object::print()" << endl; }
};
class Base :public Object
{
private:
	int num;
public:
	Base(int x=0):Object(x),num(x){}
	virtual void add() { cout << "Base::add()" << endl; }
	virtual void fun() { cout << "Base::fun()" << endl; }
	virtual void show() { cout << "Base::show()" << endl; }
};
class Test :public Base
{
private:
	int count;
public:
	Test(int x = 0) :Base(x),count(x) {}
	virtual void add() { cout << "Test::add()" << endl; }
	virtual void print() { cout << "Test::print()" << endl; }
	virtual void show() { cout << "Test::show()" << endl; }
};
int main()
{
	Object* op = nullptr;
	Object obj;
	cout << sizeof(obj) << endl;
	Base base;
	Test test;
	op = &test;
	op->print();
	return 0;
}

在这里插入图片描述
在这里插入图片描述

查虚表:

void fun(Object* op)
{
	op->add();
	op->fun();
	op->print();
	op->show(); //error
}
void func(Base* base)
{
	base->add();
	base->fun();
	base->print();
	base->show();
}

int main()
{
	Object obja;
	Base base(10);
	Test test(10);
	fun(&obja);
	fun(&base);
	fun(&test);
	func(&base);
	func(&test);
}

静态联编和动态联编

在这里插入图片描述

class Object
{
private:
	int value;
public:
	Object(int x = 0) :value(x)
	{
		memset(this, 0, sizeof(Object));
	}
	void func() { cout << "Object::fun" << value << endl; }
	virtual void add(int x) { cout << "Object::add" << x << endl; }
};

int main()
{
	Object obj;
	obj.add(10);//静态联编
	Object* op = nullptr;
	op->add(10);//动态联编,运行的时候进行查表,但是运行时程序出错,原因是this为nullptr,对空指针进行了查表操作
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/505096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

神策微报告丨10 页速览「生成式 AI」能力边界与商业化!

以 ChatGPT 为代表的生成式 AI 投入规模化应用后&#xff0c;一场人工智能的军备竞赛正在上演&#xff0c;生成式 AI 成为科技领域关注的焦点。 基于此背景&#xff0c;神策数据正式发布微报告《关于生成式 AI&#xff0c;这 10 页 PPT 就够了&#xff01;》&#xff0c;从突破…

DIDCTF平台练习-2022暑假取证学习

文章目录 前言123456789101112131415161718 前言 挺适合新手的&#xff0c;平台地址https://forensics.didctf.com/challenges 1 直接看 WIN-49I0SNRJAMF 2 计算即可 4547A61A11064DF47B272A4803788597F9A5E9AC0F11A93ABE58C8B8588956CB 3 NoxPlayer&#xff0c;夜神…

记一次azkaban调度异常处理

一、背景 预发布环境使用的数据库性能比较低&#xff0c;根据业务测试的需求&#xff0c;需要将数据库更换成 稳定高性能的数据库。更换业务数据库后azkaban定时任务失败 二、数据库服务信息 说明&#xff1a;该部分使用代号来代替&#xff0c;非真实信息 该数据库存储了azka…

docker 搭建 Elasticsearch和Kibana 8.x版本

参考: docker入门&#xff1a;单机elasticsearch安装记录&#xff0c;保证无坑_8月日更_小鲍侃java_InfoQ写作社区 新建文件夹 同上文所述相同&#xff0c;需要在宿主机上挂载配置文件与数据文件。 mkdir -p /Users/louye/data/learn-data/elastic/config mkdir -p /Users/lo…

学系统集成项目管理工程师(中项)系列19a_成本管理(上)

1. 要确保在批准的预算内完成项目 2. 必须考虑项目决策对项目产品、服务或成果的使用成本、维护成本和支持成本的影响 3. 对成本的影响力在项目早期最大 4. 失控原因 4.1. 对工程项目认识不足 4.1.1. 对信息系统工程成本控制的特点认识不足&#xff0c;对难度估计不足 4.…

大数据|实验三:PageRank算法实现

文章目录 &#x1f4da;PageRank概述&#x1f407;什么是PageRank&#x1f407;PageRank的简化模型&#x1f407;PageRank的随机浏览模型 &#x1f4da;实验目的&#x1f4da;实验平台&#x1f4da;实验内容&#x1f407;在本地编写程序和调试&#x1f407;在集群上提交作业并执…

【Linux脚本篇】流程控制语句-if

目录 &#x1f341;流程控制语句if &#x1f342;单分支语句 &#x1f342;双分支语句 &#x1f342;多分支语句 &#x1f341;流程控制语句&#xff1a;文件比较 &#x1f341;流程控制语句&#xff1a;整数比对 &#x1f341;流程控制语句&#xff1a;字符对比 &#x1f341;…

校园企业车辆维修报修管理系统设计与开发

本研究课题重点主要包括了下面几大模块&#xff1a;在本基于.net平台的车辆系统中分为管理员和用户2个模块&#xff0c;主要功能包括管理员信息管理&#xff0c;车辆信息管理&#xff0c;驾驶员信息管理&#xff0c;事故信息管理&#xff0c;维修信息管理&#xff0c;维修点管理…

【Leetcode -463.岛屿的周长 - 476.数字的补码】

Leetcode Leetcode -463.岛屿的周长Leetcode - 476.数字的补码 Leetcode -463.岛屿的周长 题目&#xff1a;给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向…

C/C++每日一练(20230509) 分割回文串II、盛水容器、Atoi

目录 1. 分割回文串 II &#x1f31f;&#x1f31f;&#x1f31f; 2. 盛最多水的容器 &#x1f31f;&#x1f31f; 3. 字符串转换整数 (atoi) &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/…

Springboot +Flowable,三种常见网关的使用(排他、并行、包容网关)(二)

一.简介 Flowable 中常用的网关主要有三种类型&#xff0c;分别是&#xff1a; 排他网关并行网关包容网关 下面来说下这三种的网关的概念和用法。 二.并行网关 并行网关&#xff0c;这种网关一般用在并行任务上&#xff0c;截图如下&#xff1a; 并行网关一般是成对出现的…

Vue学习笔记3 - Vue中 radio/select 如何设定初期值

使用 v-model 如何设定 radio的初期值呢&#xff1f; 使用v-model 绑定 value 属性&#xff0c;然后设置初始 value 属性的值即可。 比如 sexValue设置为 女&#xff0c;那么 女 那项就会被默认选中。 <!DOCTYPE html> <html lang"en"> <head>&…

数字孪生模型构建理论及应用

源自&#xff1a;计算机集成制造系统 作者&#xff1a;陶飞 张贺 戚庆林 徐 俊 孙铮 胡天亮 刘晓军 刘庭煜 关俊涛 陈畅宇 孟凡伟 张辰源 李志远 魏永利 朱铭浩 肖斌 摘 要 数字孪生作为实现数字化转型和促进智能化升级的重要使能途径&#xff0c;一直备受各…

Vue-01---初识Vue

一.搭建Vue开发环境 不建议初学者直接使用vue-cli脚手架 不建议初学者使用开发工具直接创建Vue工程 直接在html中用script引入 浏览器安装Vue Devtools插件 CDN链接引入&#xff08;不建议&#xff09;&#xff1a; <script src"h…

什么是电子负载?

1、简介 电子负载在硬件测试中是使用频率比较高的设备之一&#xff0c;是一种从电源吸收电流并消耗功率的测试仪器&#xff0c;基本都是通过控制内部功率器件&#xff08;Mosfet&#xff09;导通量&#xff0c;依靠功率管的耗散功率消耗电能。很多初入硬件或者硬件测试的小伙伴…

物联网安全工作梳理(0)

物联网相比互联网,设备更多,协议更多,标准不统一,安全更脆弱,因此相当于互联网的安全漏洞增量。物联网安全整改流程相比互联网在增量上工作更多些。本篇将从八个方面阐述物联网安全整改工作总结,每个面都是一项大工程。 物联网与互联网差异 物联网安全可分为8大类来说明…

PostgreSQL 新闻速递 谷歌基于POSTGRESQL 兼容数据库提供更大规模的数据库服务

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

国产仪器 6595A户外多通道光伏组件测试仪

6595A测试仪主机具备自主测试和显示能力&#xff0c;提供真6路电子负载&#xff0c;提供多至6通道的实时同步测试能力&#xff0c;并可根据用户需求订制通道数量及测试模块的测试功率。多台测试仪通过联网控制组成评测系统&#xff0c;可进行多至300个光伏组件的同步测试&#…

Threejs——五、点线模型对象、三角形概念、几何体顶点位置,顶点索引、法线以及对几何体进行旋转缩放和平移

文章&#xff1a; Three——一、初识Three以及基础的前端场景搭建(结尾含源码)Three——二、加强对三维空间的认识Three——三、动画执行、画布大小、渲染帧率和相机适配体验Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用Three——五、点线模型对象、三角形概念…

基于主从博弈的综合能源服务商动态定价策略研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…