菱形继承的类对父类的初始化、组合、多态、多态的原理等的介绍

server/2024/9/25 1:54:21/

文章目录

  • 前言
  • 一、菱形继承的类对父类的初始化
  • 二、组合
  • 三、 多态
    • 1. 构成多态
    • 2. 虚函数
    • 3. 虚函数的重写
    • 4. 虚函数重写的两个例外
      • 1. 协变
      • 2. 析构函数的重写
    • 5. C++11 final 和 override
      • 1. final
      • 2. override
    • 6. 设计不想被继承的类
    • 7. 重载、覆盖(重写)、 隐藏(重定义)的对比
  • 四、多态的原理
  • 总结


前言

菱形继承的类对父类的初始化、组合多态多态的原理等的介绍


一、菱形继承的类对父类的初始化

#include<iostream>
#include <string>
using namespace std;class A
{
public:A(const char* A):_a(A){cout << "class A" << endl;}string _a;
};class B : virtual public A
{
public:B(const char* A, const  char* B):A(A),_b(B){cout << "class B" << endl;}string _b;
};class C : virtual public A
{
public:C(const char* A, const char* C):A(A),_c(C){cout << "class C" << endl;}string _c;
};class D : public B, public C
{
public:D(const char* A, const char* B, const char* C, const char* D): A(A), B(A, B), C(A, C), _d(D){cout << "class D" << endl;}string _d;
};int main()
{D d("class A", "class B", "class C", "class D");return 0;
}

结构为:
在这里插入图片描述

  • 因为D类有如上的结构,所以A类会在D类中调用构造函数初始化,并且只调用一次,在D类中初始化B类和C类时,传入的A类不调用A类的构造函数初始化。

在这里插入图片描述

二、组合

  • public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
  • 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

优先使用对象组合,而不是类继承。

#include <iostream>
using namespace std;class A
{
protected:int _a;
};class B : public A
{
protected:A _bb;
};int main()
{return 0;
}

三、 多态

1. 构成多态

构成多态需要两个条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
// 构成多态
#include<iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "购票---全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(){cout << "购票---半价" << endl;}
};//void fun(Person& p)
//{
//	p.BuyTicket();
//}void fun(Person* p)
{p->BuyTicket();
}int main()
{Person p;fun(&p);Student s;fun(&s);return 0;
}

在这里插入图片描述

2. 虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数。

class Person
{
public:virtual void BuyTicket(){cout << "购票---全价" << endl;}
};

3. 虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

在重写基类的虚函数的时候,虽然派生类的虚函数不写virtual也可以构成重写, 但是不建议这样使用

#include<iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "购票---全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(){cout << "购票---半价" << endl;}
};

4. 虚函数重写的两个例外

1. 协变

基类与派生类虚函数返回值类型不同。

简单来讲就是 基类与派生类的虚函数的返回值构成父子类指针或引用关系。

#include <iostream>
using namespace std;class A {};
class B :public A {};class Person
{
public:virtual A* BuyTicket(){cout << "购票---全价" << endl;return new A;}
};class Student : public Person
{
public:virtual B* BuyTicket(){cout << "购票---半价" << endl;return new B;}
};void fun(Person& p)
{p.BuyTicket();
}int main()
{Person p;fun(p);Student s;fun(s);return 0;
}
  • 基类和派生类构成父子类指针的关系

在这里插入图片描述

2. 析构函数的重写

虽然基类和派生类的析构函数的函数名不同,但是编译器会将析构函数的函数名,都处理成destructor,因此可以构成重写。

一般情况下,应该将派生类的析构函数与基类析构函数构成函数重写,使下面的情况delete可以实现多态,保证指向的对象正确调用析构函数。

析构函数可以构成虚函数重写吗, 为什么要构成虚函数重写?

  1. 析构函数加virtual会构成析构函数,因为编译器会将析构函数的名字统一命名为destructor
  2. 构成虚函数重写是因为,我们new一个派生类对象的空间,但是用基类的类型指针接收
  3. 在析构这个对象时,只会进行普通调用,普通调用会按照当前类型, 则只会调用基类的析构函数
  4. 这种情况,我们希望是一个多态调用,按照指向的类型调用析构函数,就需要构成虚函数的重写。
#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "购票---全价" << endl;}virtual ~Person(){cout << " ~Person() " << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(){cout << "购票---半价" << endl;}virtual ~Student(){cout << " ~Student() " << endl;}
};int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2; // p2->destuctor() + operator delete(p)// 这里我们期望是一个多态调用,而不是普通调用p1 = nullptr;p2 = nullptr;return 0;
}

在这里插入图片描述

5. C++11 final 和 override

1. final

final 修饰的虚函数不能被重写

在这里插入图片描述

final修饰的类,不能被当做基类, 不能被继承

在这里插入图片描述

2. override

检查派生类中的虚函数与基类中虚函数是否构成重写,若不构成重写则报错。

#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){}};class Student : public Person
{
public:// 检查派生类中的虚函数与基类中的虚函数是否构成重写virtual void BuyTicket()override{cout << "购票---半价" << endl;}
};int main()
{Person p;return 0;
}

在这里插入图片描述

6. 设计不想被继承的类

将构造函数私有或者将析构函数私有

将构造函数私有

#include <iostream>
using namespace std;class A
{
public:static A CreateObj(){return A();}
private:A() {}};int main()
{A::CreateObj();return 0;
}

在这里插入图片描述

7. 重载、覆盖(重写)、 隐藏(重定义)的对比

在这里插入图片描述

四、多态的原理

普通调用在编译时地址就确定了
多态调用在程序运行时,到指向对象的虚函数表中找函数的地址

#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "购票---全价" << endl;}int _a = 0;};class Student: public Person
{
public:virtual void BuyTicket(){cout << "购票---半价" << endl;}int _b = 1;
};// 普通调用
//void fun(Person p)
//{
//	p.BuyTicket();
//}// 多态调用
void fun(Person& p)
{p.BuyTicket();
}int main()
{Person p;Student s;return 0;
}

在这里插入图片描述


总结

菱形继承的类对父类的初始化、组合多态多态的原理等的介绍


http://www.ppmy.cn/server/121595.html

相关文章

计算机复习9.23

关系&#xff1a;一张扁平的二维表&#xff0c;关系应该具备每个分量都不可分的数据(1NF) 候选码&#xff1a;某个属性组可以唯一标识一个元组&#xff0c;而其子集不能&#xff0c;候选码中的属性叫主属性 主码&#xff1a;从候选码中选取一个称为主码 全码&#xff1a;所有…

C#设计模式之访问者模式

总目录 前言 在软件构建过程中&#xff0c;由于需求的改变&#xff0c;某些类层次结构中常常需要增加新的行为&#xff0c;如果直接在基类中做这样的更改&#xff0c;将会给子类带来很繁重的变更负担&#xff0c;甚至破坏原有设计。如何在不更改类层次结构的前提下&#xff0c…

数据脱敏-快速使用

1.数据脱敏定义 数据脱敏百度百科中是这样定义的&#xff1a; 数据脱敏&#xff0c;指对某些敏感信息通过脱敏规则进行数据的变形&#xff0c;实现敏感隐私数据的可靠保护。 因为在真正的生产环境中,很多数据是不能直接返回,但是我们工作的时候可能经常性的需要返回一些用户信…

LabVIEW提高开发效率技巧----VI服务器和动态调用

VI服务器&#xff08;VI Server&#xff09;和动态调用是LabVIEW中的两个重要功能&#xff0c;可以有效提升程序的灵活性、模块化和可扩展性。通过这两者的结合&#xff0c;开发者可以在运行时动态加载和调用VI&#xff08;虚拟仪器&#xff09;&#xff0c;实现更为复杂的应用…

【速成Redis】04 Redis 概念扫盲:事务、持久化、主从复制、哨兵模式

前言&#xff1a; 前三篇如下&#xff1a; 【速成Redis】01 Redis简介及windows上如何安装redis-CSDN博客 【速成Redis】02 Redis 五大基本数据类型常用命令-CSDN博客 【速成Redis】03 Redis 五大高级数据结构介绍及其常用命令 | 消息队列、地理空间、HyperLogLog、BitMap、…

项目实战 (15)--- 代码区块重构及相关技术落地

目录 背景 思想与技术方案 概述 路由及socket 封装模式 技术描述 方案 1 方案2 各类连接资源管理封装 vector db connection cache management web socket 管理 service 封装 代码实现 service 层 router层 resource层 小结 背景 到目前为止,视频搜索系统功…

惊艳到你的算法

场景一 小时候去商场玩时&#xff0c;爸爸妈妈经常告诉我&#xff0c;如果你在商场走丢了&#xff0c;千万不要到处乱跑&#xff0c;站在原地不动就好了&#xff0c;我们会来找你的。 长大后学了计算机才明白&#xff0c;原来那个时候爸爸妈妈就已经会用DFS算法。如果我在原地…

招联金融2025秋招--大量招后台、算法

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策划 产品运营…