03c++继承与多态

news2024/11/14 13:57:16

目录:

  • 继承的本质和原理
  • 派生类的构造过程
  • 重载覆盖 隐藏
  • 静态绑定和动态绑定
  • 多态 vfptr和vftable
  • 抽象类的设计原理
  • 多重继承以及问题
  • 虚基类 vbptr和vbtable
  • RTTI
  • c++四种类强转
  • 继承多态常见笔试面试题目分享

1、继承的本质和原理:

继承方式: 基类的访问限定 派生类的访问限定 main外部的访问限定
三种:私有全私有
保护二级私有
公有一级私有
class A
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;
};


class B :public A
{
public:
	void func()
	{
		cout << "func: " << func << endl;
	}
	int md;
protected:
	int me;
private:
	int mf;
};

class C :public B
{
	//在c里面,请问ma的访问限定的是什么?
	//私有的

};

2、派生的构造过程

* 派生类如何初始化基类继承来的成员变量?
* 通过调用基类相应的构造函数
*
* 派生类的构造函数和析构函数负责初始化和清理派生类部分
#if 0
/*
* 派生类如何初始化基类继承来的成员变量?
* 通过调用基类相应的构造函数
*
* 派生类的构造函数和析构函数负责初始化和清理派生类部分
*/
class Base
{
public:
	Base(int data = 10) :ma(data) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }

protected:
	int ma;
};

class Derive :public Base
{
public:
	Derive(int data = 20) :mb(data), Base(data)
	{
		cout << "Derive()" << endl;
	}
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;

};

int main()
{
	Derive v;
	/*
	输出:
		Base()
		Derive()
		~Derive()
		~Base()
	先调用派生类的析构函数
	*/
	return 0;
}
#endif

3、重载 隐藏 覆盖

图像分析:
在这里插入图片描述
在这里插入图片描述
从图中可以看出来有危险。

/*重载 隐藏 覆盖
1、重载关系
必须处在同一个作用域中,函数名字相同,参数列表不同
2、隐藏:作用域的隐藏
*/

class Base
{
public:
	Base(int data = 10) :ma(data) {}
	void show() { cout << "Base::show()" << endl; }
	void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) {}
	void show() { cout << "Derive::show" << endl; }


private:
	int mb;
};

int main()
{
#if 0
	Derive d;
	d.show();
	d.Base::show(); //这样子才能调用基类的
	//d.show(10); 优先查看派生类自己的作用域成员,没有的话才去基类里面找
	d.Base::show(10);
#endif 
	Base b(10);
	Derive c(20);
	//基类到派生类的转换
	b = c; //类型从下到上的转化

	//d = b;//类型从上到下的转换 error 

	//基类指针 引用 <-派生类对象 默认派生类到基类的转换
	Base* pb = &c; //只能访问派生基类部分的成员
	pb->show();
	pb->show(10);
	/*Base::show()
	Base::show(int)
	*/

	((Derive*)pb)->show(); //这样子就调用了派生类的show

	//在继承结构中进行上下的类型的转换,默认支持从下到上的转换
	Derive* pd = (Derive*)&b; //派生类的show方法 没有派生类对象 
	//b是基类哇 不安全涉及内存的非法访问
	pd->show();
	return 0;
}

4、虚函数、静态绑定、动态绑定

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


/*
* 虚函数 ,静态绑定 和动态绑定
* 总结一:如果类里面定义了虚函数,那么编译阶段,编译器给这个类类型产生了一个唯一的vftable虚函数表,
*虚函数表中的主要存储的内容就是RTTI指针和虚函数的地址 ,程序运行中,每一张虚函数表都会记载到内存的.rodata区
*
* 总结二:
* 一个类里面定义了函数,那么这个类定义的对象运行的时候,内存中开始 部分,多存储了vfptr虚函数指针,指向相应类型 的虚函数表vftable
* 一个类型定义的n个对象,他们的vfptr指向都是同一张虚函数表
*
* 总结三:
* 一个类里卖弄虚函数的个数,不影响对象内存大小(vfptr),影响的是虚函数表的大小
*
* 总结四:
* 如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,
* 而且基类的方法都是virtual虚函数,那么派生类的这个方法,自动处理成虚函数
*/

class Base
{
public:
	Base(int data = 10) :ma(data) {}
	//虚函数
	virtual void show() { cout << "Base::show()" << endl; }
	//虚函数
	virtual void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) {}
	void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数


private:
	int mb;
};

int main()
{
	Derive d(50);
	Base* pb = &d;
	pb->show(); //静态(编译时期)的绑定 (函数的调用)
	//如果发现是虚函数,就进行动态绑定了
	pb->show(10);
	/*
	E8 0B EE FF FF       call        Base::show (07FF6900A13F7h)
	BA 0A 00 00 00       mov         edx,0Ah
	48 8B 4D 28          mov         rcx,qword ptr [pb]
	E8 E2 EA FF FF       call        Base::show (07FF6900A10DCh)
	*/

	cout << sizeof(Base) << endl;
	cout << sizeof(Derive) << endl;

	cout << typeid(pb).name() << endl;

	/*如果Base没有虚函数,*pb识别的就是编译时期的 如果有虚寒申诉就是运行时期的*/
	cout << typeid(*pb).name() << endl;
	/*
	Derive::show
	Base::show(int)
	16
	24
	class Base * __ptr64
	class Derive
	| vptr (8 字节) | ma (4 字节) | 填充 (4 字节) |

	*/
	return 0;
}
/*
* Base::show()
Base::show(int)
4
8
class Base * __ptr64
class Base

*/

5、多态

/*
解释多态:
静态编译时期:函数重载
编译阶段就已经确定好的;
动态编译时期:
在继承中,基类指针引用指向派生类的对象,通过该指针引用调用同名的覆盖方法(虚函数)基类指针指向哪个派生类对象,就会调用哪个派生类对象同名覆盖的方法,称为多态。
访问谁的vfptr=>继续访问谁的vftable;调用对应的派生类对象的方法
*/

class Animal
{
public:
	Animal(string name) :_name(name) {}
	virtual void  bark()
	{

	}
protected:
	string _name;
};

class Cat :public Animal
{
public:
	Cat(string name) :Animal(name) {}
	void bark() { cout << "猫叫" << endl; }
};

class Dog :public Animal
{
public:
	Dog(string name) :Animal(name) {}
	void bark() { cout << "狗叫" << endl; }
};

void bark(Animal* p)
{
	p->bark();
}
int main()
{
	Cat a("wocao");
	Dog b("shabi");
	bark(&a);
	bark(&b);
	return 0;
}

6、理解抽象类

//汽车的基类
class Car
{
public:
	Car(string name, double oil) :name(name), oil(oil) {}
	double getLiftMiles()
	{
		return oil * getMilesPersonGallon();
	}
public:
	string name;
	double oil;
	virtual double getMilesPersonGallon() = 0; //纯虚函数

};

class Bnze :public Car
{
public:
	Bnze(string name, double oil) :Car(name, oil) {}
	double getMilesPersonGallon() { return 20.0; }
};

class Audi :public Car
{
public:
	Audi(string name, double oil) :Car(name, oil) {}
	double getMilesPersonGallon() { return 18.0; }
};

void  showCarLeftMiles(Car& car)
{
	cout << "Car name:" << car.name << endl << car.getLiftMiles() << endl;

}
int main()
{
	Audi car("1", 12);
	Bnze car1("2", 12);
	showCarLeftMiles(car);
	showCarLeftMiles(car1);
	return 0;
}

7、再谈动态绑定

/*虚函数和动态绑定 问题:是不是虚函数的调用一定就是动态绑定? 不是
类的构造函数中,调用虚函数不会发生动态绑定
*/


class Base
{
public:
	Base(int data = 10) :ma(data) {}
	//虚函数
	virtual void show() { cout << "Base::show()" << endl; }
	//虚函数
	virtual void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) {}
	void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数


private:
	int mb;
};
int main()
{
	Base b;
	Derive d;
	/*
	E8 6F E9 FF FF       call        Base::Base (0121118h)
	6A 14                push        14h
	8D 4D DC             lea         ecx,[d]
	E8 24 E9 FF FF       call        Derive::Derive (01210D7h)
	对象本身调用虚函数是静态绑定
	*/
	b.show();
	d.show();

	//动态绑定
	Base* pb1 = &b;
	pb1->show();
	Base* pb2 = &d;
	pb2->show();

	//动态绑定
	Base& rb1 = b;
	rb1.show();
	Base& rb2 = d;
	rb2.show();

	//动态绑定
	Derive* p = (Derive*)&b;
	p->show();
	//如果不是引用或者指针调用则是静态调用
	return 0;
}

8、虚析构函数

/*

/*
1、哪些函数不能实现虚函数
虚函数依赖:
	1虚函数能产生地址,存储在虚函数表vftable中
	2、对象必须存在(vfptr->vftable->虚函数地址)

构造函数:
1、virtual + 构造函数
2、构造函数中华调用的任何函数都是静态绑定的调用虚函数,也不会产生静态绑定派生类的对象构造过程。
1、先调用的是基类的构造函数2、才调用派生类的构造函数

static静态成员方法 No virtual + static

问题二:
虚析构函数 析构函数调用的时候存在,对象是存在的!
什么时候吧基类的析构函数必须实现成虚函数?
基类的指针(引用)指向堆上new出来的派生类对象的时候,delete pb(基类的指针)
,它调用析构函数的时候必须发生动态绑定,否则会导致派生类的析构函数无法调用
*/


class Base
{
public:
	Base(int data = 10) :ma(data) { cout << "Base()" << endl; }
	virtual ~Base() { cout << "~Base()" << endl; }
	//虚函数
	virtual void show() { cout << "Base::show()" << endl; }
	//虚函数
	virtual void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) { cout << "Derive()" << endl; }
	//基类的析构函数是虚函数,派生类的析构函数默认成为虚函数
	~Derive() { cout << "~Derive()" << endl; }
	void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数


private:
	int mb;
};

int main()
{
	Base* pb = new Derive(10);
	pb->show();//动态绑定
	delete pb;//派生类的析构函数,没有呗调用
	/*
	pb->Base Base::~Base 对于析构函数的调用就是静态绑定了

	发现是虚析构函数就是动态绑定了
	call Base::~Base
	*/
	return 0;
}

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

9、题目分享

class Animal
{
public:
	Animal(string name) :_name(name) {}
	virtual void  bark() = 0;
protected:
	string _name;
};

class Cat :public Animal
{
public:
	Cat(string name) :Animal(name) {}
	void bark() { cout << "猫叫" << endl; }
};

class Dog :public Animal
{
public:
	Dog(string name) :Animal(name) {}
	void bark() { cout << "狗叫" << endl; }
};

int main()
{
	Animal* p1 = new Cat("加菲猫");
	Animal* p2 = new Dog("二哈");

	int* p11 = (int*)p1;

	int* p22 = (int*)p2;
	int tmp = p11[0]; //cat的前四个字节 vfptr -> Dog vftable
	p11[0] = p22[0];  //dog的前四个字节 vfprt ->Cat vftable
	p22[0] = tmp;

	p1->bark(); //cat vfptr -> dog vfptr->dog vftable
	p2->bark();
	delete p1;
	delete p2;
	return 0;
}
class Base
{
public:
	virtual void show(int i = 10)
	{
		cout << "call Base::show i" << i << endl;
	}
};

class Derive :public Base
{
public:
	void show(int i = 20)
	{
		cout << "call Derive ::show i " << i << endl;
	}
};

int main()
{
	Base* p = new Derive();//虚析构函数

	/*
	* push 0Ah =》函数调用参数压栈的在编译时期就确定好的 是基类的10
	* mov eax,dword ptr[p];
	* mov ecx,dword ptr[eax]
	* call ecx;
	*/
	p->show();
	delete p; //动态绑定 p->Derive vfptr ->Derive vftable

	return 0;
}
class Base
{
public:
	virtual void show()
	{
		cout << "call Base::show " << endl;
	}
};

class Derive :public Base
{
private: //改成私有还能调用吗?
	void show()
	{
		cout << "call Derive ::show " << endl;
	}
};

int main()
{
	Base* p = new Derive();
	/*
	* 成员方法能不能调用就是说方法的访问权限是不是public,实在编译时期就需要确定的
	* 只能看到Base::show
	* 基类:call Base::show静态
	* call Derive::show动态
	*/
	p->show(); //最终调用是在运行时期才确定的 这个调用的call Derive show()
	delete p;
	return 0;
}

class Base
{
public:
	Base()
	{
		/*
		push ebp
		mov ebp,esp
		sub esp,4Ch
		rep  stos esp<->ebp
		vfptr <= &Base::vftable
		*/
		cout << "call Base()" << endl;
		clear();
	}
	void clear() { memset(this, 0, sizeof(*this)); }

	virtual void show()
	{
		cout << "call Base::show()" << endl;
	}
};

class Derive :public Base
{
public:
	/*
	push ebp
	mov ebp,esp
	sub esp,4Ch
	rep  stos esp<->ebp
	vfptr <= &Derivev::vftable
	*/
	Derive()
	{
		cout << "call Derive ()" << endl;
	}

	void show()
	{
		cout << "call Derive::show()" << endl;
	}

};

int main()
{
	//Base* p = new Base();
	//p->show();//动态绑定
	//delete p;

	Base* p1 = new Derive();
	p1->show(); //动态绑定
	delete p1;
	return 0;
}

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

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

相关文章

2023愚人杯 )————被遗忘的反序列化

<?php# 当前目录中有一个txt文件哦 error_reporting(0); show_source(__FILE__); include("check.php");class EeE{public $text;public $eeee;public function __wakeup(){if ($this->text "aaaa"){echo lcfirst($this->text);}}public functi…

如何远程操作服务器中的Python编译器并将运行结果返回到Pycharm

文章目录 一、前期准备1. 检查IDE版本是否支持2. 服务器需要开通SSH服务 二、Pycharm本地链接服务器测试1. 配置服务器python解释器 三、使用内网穿透实现异地链接服务器开发1. 服务器安装Cpolar2. 创建远程连接公网地址 四、使用固定TCP地址远程开发 本文主要介绍如何使用Pych…

1065 单身狗

solution “单身狗”&#xff1a;没有搭子或者搭子没来的参与者输出id时按增序输出 #include<iostream> #include<map> #include<algorithm> using namespace std; const int maxn 1e6, maxn1 1e4 10; int flag[maxn] {0}, all[maxn1]; int main(){int…

工程师工具箱系列(2)hasor

文章目录 工程师工具箱系列(2)hasor简介特点环境准备引入依赖数据库脚本文件配置Hasor配置 运行测试小结 工程师工具箱系列(2)hasor 简介 Hasor有着自己的独立的生命周期与Spring的不同&#xff0c;是一套完整的体系&#xff0c;提供了注入DataQL、Dataway、hasor-web等等&am…

《中阿科技论坛(中英文)》是什么级别的期刊?是正规期刊吗?

问题解答 问&#xff1a;《中阿科技论坛&#xff08;中英文&#xff09;》是核心期刊吗&#xff1f; 答&#xff1a;不是&#xff0c;但是正规期刊 问&#xff1a;《中阿科技论坛&#xff08;中英文&#xff09;》是什么级别期刊&#xff1f; 答&#xff1a;省级 主管单位…

十三、Redis哨兵模式--Sentinel

上一篇介绍了Redis中的主从复制。我们知道Redis主从中一般只有主节点对外提供写操作&#xff0c;如果主节点发生故障&#xff0c;为了保证Redis的可用性&#xff0c;这时就要在可用的slave节点中&#xff0c;挑选一个作为主节点。这种切换操作如果是人为的操作&#xff0c;那么…

i春秋-GetFlag

题目 考点 sql注入&#xff0c;md5加密&#xff0c;代码审计&#xff0c;利用eval函数 解题 参考wp https://www.cnblogs.com/qiaowukong/p/13630130.html找md5值 看见验证码中的提示&#xff0c;就是去找一个md5值前六位是指定值的数&#xff08;严格来说不一定是数&…

基于python的旅游爬虫可视化与实现

摘要 本项目为基于python的旅游爬虫可视化的设计与实现&#xff0c;项目以Web系统形式展示&#xff0c;利用Xpath爬虫爬取去哪儿网针对旅游业的需求&#xff0c;对国内热门旅游景点数据可视化系统&#xff0c;将爬取好的数据保存为CSV文件&#xff0c;再通过ORM框架导入MySQL数…

出现Duplicate key

解决&#xff1a; 第一种情况&#xff1a; 添加一个字段prjId &#xff0c;和数据库表映射时&#xff0c;映射的字段存在映射关系了。 将第二个 TableField中的prj_num改成prj_id 即可。 第二种情况&#xff1a; 转成map的形式时&#xff1a;key重复了&#xff0c;不知道把值赋…

QuickBooks 2024 for Mac 激活版:智慧管理,财务无忧

想要轻松掌控财务&#xff0c;实现高效管理吗&#xff1f;QuickBooks 2024 for Mac&#xff0c;您的智慧财务管理专家&#xff0c;为您带来前所未有的便利和体验。无论是账务、工资还是销售和库存&#xff0c;它都能一手搞定。直观易用的界面&#xff0c;让您轻松上手&#xff…

(二)Jetpack Compose 布局模型

前文回顾 &#xff08;一&#xff09;Jetpack Compose 从入门到会写-CSDN博客 首先让我们回顾一下上一篇文章中里提到过几个问题&#xff1a; ComposeView的层级关系&#xff0c;互相嵌套存在的问题&#xff1f; 为什么Compose可以实现只测量一次&#xff1f; ComposeView和…

加密与CA证书

文章目录 加密与CA证书http协议是不安全的使用对称秘钥进行数据加密非对称秘钥加密CA证书应用补充 加密与CA证书 CA 证书是什么&#xff0c;证书的目的是什么 首先明确一点&#xff0c;CA证书是数字时代中确保身份和数据安全的重要工具&#xff0c;为用户提供了安心、便捷和可…

vlock工具:锁定Linux终端的安全智能方法

虚拟控制台是 Linux 非常重要的功能&#xff0c;它们为系统用户提供 shell 提示&#xff0c;以非图形设置方式使用系统&#xff0c;该设置只能在物理机上使用&#xff0c;而不能远程使用。 用户只需从一个虚拟控制台切换到另一个虚拟控制台即可同时使用多个虚拟控制台会话。 …

[机器学习-05] Scikit-Learn机器学习工具包进阶指南:协方差估计和交叉分解功能实战【2024最新】

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

QT自适应界面 处理高DPI 缩放比界面乱问题

1.pro文件添加 必须添加要不找不到 QT版本需要 5。4 以上才支持 QT widgets 2.main界面提前处理 // 1. 全局缩放使能QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);// 2. 适配非整数倍缩放QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::High…

EPAI手绘建模APP工程图顶部工具栏

7、工程图 图 302 工程图 工程图包括顶部常用工具栏、右侧工程图工具栏、左侧模型列表栏、中间的工程图。 (1) 常用工具栏 ① 删除&#xff0c;选中场景中工程图元素后&#xff0c;删除。可以选择多个工程图元素同时删除。 ② 设置&#xff0c;打开工程图设置页面&#xff0…

科林Linux_4 信号

#include <signal.h> 信号signal&#xff1a;Linux或Unix系统支持的经典的消息机制&#xff0c;用于处置进程&#xff0c;挂起进程或杀死进程 kill -l #查看系统支持的信号 1~31 Unix经典信号&#xff08;软件开发工程师&#xff09; 34~64 自定义信号/实时信号&am…

pycharm 将项目连同库一起打包及虚拟环境的使用

目录 一、创建虚拟环境 1、用 anaconda 创建 2、Pycharm 直接创建 二、虚拟环境安装第三方库 1、创建项目后&#xff0c;启动终端(Alt F12)&#xff0c;或者点击下方标记处。 2、使用 pip 或者 conda 来进行三方库的安装或卸载 3、将项目中的库放入文档&#xff0c;便于…

Python深度学习基于Tensorflow(1)Numpy基础

文章目录 数据转换和数据生成存取数据数据变形和合并算数计算广播机制使用Numpy实现回归实例 numpy的重要性不言而喻&#xff0c;一般不涉及到GPU/TPU计算&#xff0c;都是用numpy&#xff0c;常见的np就是这个玩意。其特点就是快&#xff01;其实如果不涉及到深度学习&#xf…

【挑战30天首通《谷粒商城》】-【第一天】01、简介-项目介绍

文章目录 课程介绍一、 项目介绍1、项目背景A、电商模式1、B2B 模式2、B2C 模式3、C2B 模式4、C2C 模式5、O2O 模式 1.2、项目架构图1.3、项目技术 & 特色1.4、项目前置要求二、分布式基础概念(略)三、环境撘建(略) one more thing 课程介绍 1.分布式基础(全栈开发篇)2.分…