04.17_111期_C++_继承多态_虚表

ops/2025/2/16 6:30:35/

 如果想实现不同的类的对象调用不同的虚函数
 可以通过   协变   这种形式
 在父类Person中的虚函数的返回值 的数据类型是一个  父类(任意一个父类σ就行)
 在子类Student中的虚函数的返回值 的数据类型是一个  继承了σ的子类

class Student : public Person
{
public:virtual B* BuyTicket(){cout << "买票-半价" << endl;return nullptr;}//如果不写 virtual 下述析构函数//那么只要定义一个 非Student 类型的对象,就无法调到~Student()这个函数//导致 _ptr无法释放空间 virtual ~Student(){delete[] _ptr;cout << "~Student()" << endl;}
private:int* _ptr = new int[10];
};
//实际上  父子类  中析构函数的函数名最终会处理成一样的
//那么是否应该使用virtual来修饰自定义的 父子类中的析构函数呢?
int main()
{Person* p1 = new Person;Person* p2 = new Student;//delete这条指令代表了 析构函数 和 operator delete(p1)//delete在调用析构函数的时候只会看两点/*1. 根据指针的类型,如果是Person类型的指针,此时无论指向的是什么,都会调用Person这个类中的析构函数 ~Person2. 根据父子类中有没有对应的  自定义 虚 的析构函数如果有,且构成多态,那么就是指向谁,析构谁,比如上述的代码中p2指向的是一个Student对象,那么此时delete p2,将会调用Student的析构函数 virtual ~Student()注意!!!!!!!!!!!!!!!!!!!!!!!!一、如果只通过  1  来调用析构函数,那么可能会造成无法调到指定类型的析构函数问题这是因为指针的类型不一定和 所指向的 数据类型相同根据指针类型  来调用 析构函数那么只能通过 在父子类中对析构函数分别加上 virtual二、上述一、中给父子类的析构函数加上 virtual就能构成多态的原因是:编译器会把所有析构函数都处理成同名同参的函数,都改成destructor()的函数以方便这两个函数构成重写*/delete p1;delete p2;return 0;
}

综上,虚函数virtual的使用有三个例外
      1. 协变:父子类中不完全三同, 返回值类型 是父子类
      2. 父子类中析构函数都加上virtual
    3. 子类中的虚函数可以不加virtual

例题1:
对于下面的程序,其执行结果是 B->1,需要注意的是
      1. 首先有一个指p 指向了 一个B 类对象,
         对于p->test(); 值得说明的是:
         1.1 由于 p 是一个 B类型的指针,所以 会在 B 类型中寻找一个名叫test()的成员函数
         1.2 B类继承了A,要找到test(),也可以从 A 类中寻找
      2. A类中的test()要调用 func(),那么需要问,构不构成多态调用?
         构成,这是因为:
         2.1 父类和子类中都有func,且返回值,且参数类型相同,且父类中的func用virtual修饰
         2.2 调用func的时候是通过 A类中this指针进行调用的,
             值得注意的是:
             this指针的数据类型是 A*,这是因为test()是属于A这个类域的
         所以构成多态调用
      3. 基于2.,由于A::this->func();构成多态调用
         所以会根据 指针指向的对象来确定调用哪个函数,
         注意:和什么类型的指针没有关系,只取决于被指向的数据类型,
         也就是只取决于一开始在调用test()这个函数时,
         写下σ->test()的这个σ究竟是什么类型,对于下面的程序
         σ的类型是有new B 来决定的,σ的类型是B
         所以应该调用B中的func函数
    4. 虽然会执行 cout << "B->" << val << endl; 的这个定义
         但是函数的声明是  virtual void func(int val = 1) 
         这就是多态调用的特点,需要按照3. 先找到σ的类型,把这个类型中的func找到后
         把其中的实现代码  重写  到 2.中用 this 指针进行调用的 func() 函数中
         也就是 (根据2.) 使用 A 中func的声明,(根据3.) 使用 B 中 func的定义

例题2:
 多继承中由于public Base1, public Base2 表明
 Base1比Base2先继承,那么Base1 的会先开空间
 所以Base1先开空间,Base2后开空间
 Derive会指向包含父类空间和子类所需空间的一开始,
 所以 p3 指针变量的值和 p1 指针变量的值相同,且小于p2


http://www.ppmy.cn/ops/26829.html

相关文章

TLS:互联网通信的安全基石

传输层安全性&#xff08;Transport Layer Security&#xff0c;TLS&#xff09;是一种广泛使用的网络安全协议&#xff0c;用于在互联网上提供隐私和数据完整性。TLS通常用于Web浏览器和服务器之间、邮件服务器之间以及其他需要安全通信的场景。本文将介绍TLS的原理、建立连接…

JAVA读取从WPS在Excel中嵌入的图片资源

读取从WPS在Excel中嵌入的图片资源 引言 许多数据文件中可能包含嵌入式图片&#xff0c;这些图片对于数据分析和可视化非常重要。然而&#xff0c;从 WPS 在 Excel 中读取这些图片可能会有一些技术挑战。在本文中&#xff0c;我将展示如何从 WPS Excel 文件中读取嵌入的图片&am…

明德扬逻辑设计基本功修炼课考试体会_我的FPGA成长篇

本文为明德扬原创文章&#xff0c;转载请注明出处&#xff01;作者&#xff1a;明德扬学员&#xff1a;考试酷账号&#xff1a;11167760 大家好&#xff0c;我是基本功修炼班的学员。从2023年8月开始&#xff0c;我参加了考试酷&#xff0c;并且已经获得了全套项目课程奖励&am…

qt嵌入并控制外部程序

一、流程 1、调用Window接口模拟鼠标&#xff0c;键盘事件 POINT point; LPPOINT lpppoint &point; GetCursorPos(lpppoint);//获取鼠标位置 SetCursorPos(point.x, point.y);//设置鼠标位置//鼠标左键按下 mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, poi…

go语言获取变量类型的4种方式

在go语言中我们常常需要获取某个变量的类型&#xff0c;其他语言如python可以使用 type(x), javascript中可以使用 typeof x 获取变量类型&#xff0c; Go 语言中我们也可以通过一下4种方式获取变量的类型。 1. 通过 fmt.Printf 的 %T 打印变量的类型&#xff1b; var x flo…

Podman与Docker有何不同?

Podman与Docker在安全性、守护进程和兼容性方面存在显著差异。具体如下&#xff1a; 安全性&#xff1a; Docker需要root权限才能运行守护进程&#xff0c;这可能导致安全风险&#xff0c;因为配置不当的容器可能会获得对主机文件系统的无限制访问。Podman允许以非特权用户启…

前端科举八股文-CSS篇

前端科举面经-CSS篇 Css选择器的优先级css盒模型行内元素和块级元素的区别?link标签和import标签的区别讲一下弹性盒子布局的常见属性flex是哪三个属性的简写什么是BFC&#xff1f; 有什么作用垂直居中的方法?visibilityhidden, opacity0&#xff0c;display:none的区别清除浮…

数据结构——二叉树(堆)

二叉树顺序结构以及实现 普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费&#xff0c;因此完全二叉树更适合使用顺序结构来存储。 二叉树一般有两种结构存储: 顺序结构和链式结构。 顺序结构也就是顺序表(数组)来存储&#xff0c;一般只有完全二…