C++修炼之路之继承<二>

news/2024/10/22 16:45:50/

目录

一:子类的六大默认成员函数 

二:继承与友元

三:继承与静态成员

四:复杂的继承关系+菱形继承+菱形虚拟继承 

1.单继承

2.多继承 

3.菱形继承;一种特殊的多继承 

4.菱形虚拟继承

5.虚拟继承解决数据冗余和二义性的原理 

不使用虚拟继承前数据冗余的情况

使用虚拟继承数据的情况

经典例题

五:继承和组合

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧

衔接上篇的内容继续介绍

一:子类的六大默认成员函数 

1.默认的意思就是我们不写,编译器会自动生成一个,在子类中的默认成员函数要把父类单独看成一个独立的对象来完成对应操作

父类的默认函数

class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};

实现子类的对应函数操作

class Student :public Person
{
public:Student(const char* name,int id):Person(name)//作为一个单独对象构造,_id(id){cout << "Student(const char* name, int id)" << endl;}Student(Student& s):Person(s)//父子类的赋值规则, _id(s._id){cout << "Student(const Student& s)" << endl;}Student& operator=(Student& s){if (this != &s){_id = s._id;Person::operator = (s);//父子类的赋值规则}cout << "Student& operator=(const Student& s)" << endl;return *this;}~Student(){//Person::~Person();cout << "~Student()" << endl;}
protected:int _id;
};

 特殊的对于子类此处的析构函数,不能显示调用父类的析构,原因为

1.由于多态的原因,析构函数统一会被处理成destructor

2.父子类的析构函数构成隐藏

3.为了保证析构安全,先子后父(如果先父后子的话,在父类析构后,在子类还可以访问父类的成员,此时就会导致访问已经释放的空间,导致越界访问),构造为先父后子

4.父类析构函数不需要显示调用,子类析构函数结束时会自动调用父类析构,保证先子后父

练习:如何实现一个不能被继承的类

方法为:可以将父类的成员函数或者整个成员访问方式设置为private,这样继承的时候无论继承方式为那种,都是不可见的

在c++11中新添加了final关键字,使用的话意味着该类不能被继承

二:继承与友元

友元关系不能继承,基类友元不能访问子类私有和保护成员

如要访问子类的私有和保护成员的话,在子类中也声明友元关系(就是基类的友元声明在派生类声明一下)

三:继承与静态成员

基类中定义了static静态成员,则整个继承体系里面只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例

练习:计算创建多少个类对象--子类初始化时不管我们写不写都会走初始化列表,也一定会走父类的构造

class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};Student func()
{Student st;return st;
}int main()
{Person p;Student s;func();cout << Student::_count << endl;return 0;
}

四:复杂的继承关系+菱形继承+菱形虚拟继承 

1.单继承

一个子类只有一个直接父类

2.多继承 

一个子类有两个或以上直接父类

3.菱形继承;一种特殊的多继承 

从这里就可以看出,在Assistant的对象中就有两份Person成员,这就导致数据冗余和二义性的问题,这样在访问数据的时候就不知道具体访问哪一个

4.菱形虚拟继承

于是提出了使用虚继承即添加关键字virtual来解决,这是就只有一份Person成员,只初始化一次

如:

class Person
{
public:string _name; // 姓名int _age;int _tel;// ...
};
class Student : virtual public Person
{
protected:int _num; //学号
};
class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};void Test()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;a.Student::_name = "小张";a.Teacher::_name = "老张";a._name = "张三";
}int main()
{Test();return 0;
}

5.虚拟继承解决数据冗余和二义性的原理 

不使用虚拟继承前数据冗余的情况

发现在B子类 和C子类中都有一份父类A的数据,造成数据冗余

使用虚拟继承数据的情况

 这时父类的数据只有一份,子类访问的都是同一父类的数据

这时就会看到在 添加virtual的子类中会多存储一个地址,这个地址其实存储的是距离A的偏移量

此时A同属于B和C,这样B和C该如何找到公共的A呢,这里是通过了B和C的两个指针,指向的一张表,这两个指针叫虚基表指针,这两个表叫虚基表,虚基表中存的是偏移量,通过偏移量找到A

这个偏移量就是从该位置向后跳跃的字节数,然后找到并访问父类的成员数据 

经典例题

 

对于p1和p3指向的位置相同,但访问权限不同 

打印顺序为什么?

对于任何的派生类的构造都要 调用基类的构造

注意:初始化列表出现的顺序是按声明顺序来执行的,不是执行顺序

所以结果为 class A,class B ,class C,class D

一般不建议设计出菱形继承

五:继承和组合

1.public继承是一种is-a的关系,就是说每个派生类对象都是一个基类对象

2.组合是一种has-a的关系,假设B组合了A,每个B对象中都有一个A对象

对于这两个的大小都是一样大的, 

但对于继承父类中的protected 成员可以在子类中访问

对于组合的话,一个类中的protected成员,另一个类不能使用,不能直接调用另一个类的public成员函数

3.

4.实际尽量多去使用组合,组合的耦合度低,代码的可维护性好。对于有些关系就适合继承就使用继承,对于多态,也必须实现继承,对于既能用组合又能继承的,就使用组合 


http://www.ppmy.cn/news/1425760.html

相关文章

MySQL 8.0 vs MySQL 5.7: 详细比较

MySQL 8.0 vs MySQL 5.7: 详细比较 MySQL是世界上最流行的开源关系数据库之一&#xff0c;随着技术的进步&#xff0c;每个新版本都带来了许多重要的更新和改进。在本文中&#xff0c;我们将深入探讨MySQL 8.0和5.7两个版本之间的主要差异&#xff0c;涵盖从性能改进、新特性到…

如何用JAVA如何实现Word、Excel、PPT在线前端预览编辑的功能?

背景 随着信息化的发展&#xff0c;在线办公也日益成为了企业办公和个人学习不可或缺的一部分&#xff0c;作为微软Office的三大组成部分&#xff1a;Word、Excel和PPT也广泛应用于各种在线办公场景&#xff0c;但是由于浏览器限制及微软Office的不开源等特性&#xff0c;导致…

Spring Boot 中 Controller 接口参数注解全攻略与实战案例详解

引言 在 Spring Boot 应用程序中&#xff0c;Controller 是 MVC 架构模式中的核心组件之一&#xff0c;负责处理 HTTP 请求并返回响应结果。为了更好地映射请求、解析请求参数、执行业务逻辑和生成视图或 JSON 数据&#xff0c;Controller 中广泛使用了各种注解。本文将全面梳…

现代软件为什么要采用微服架构

现代软件采用微服务架构是为了解决传统单体架构在开发、部署和维护大型应用时面临的一系列问题。以下是采用微服务架构的主要优势&#xff1a; 1. **模块化和组件化**&#xff1a;微服务通过将应用拆分为一系列小型、松耦合的服务来提高模块化水平。每个服务都是围绕特定的业务…

【蓝桥杯 2020 国 B】美丽的 2 题解(Excel+提交答案)

问题描述 小蓝特别喜欢 2, 今年是公元 2020 年&#xff0c; 他特别高兴。他很好奇&#xff0c; 在公元 1 年到公元 2020 年&#xff08;包含&#xff09;中&#xff0c; 有多少个年份的数位中包含数字 2? 答案提交 这是一道结果填空的题&#xff0c; 你只需要算出结果后提交…

面试: 悲观锁和乐观锁

一、悲观锁的代表是synchronized和Lock 锁 其核心思想是【线程只有占有了锁&#xff0c;才能去操作共享变量&#xff0c;每次只有一个线程占锁成功&#xff0c;获取锁失败的线程&#xff0c;都得停下来等待】线程从运行到阻塞、再从阻塞到唤醒&#xff0c;涉及线程上下文切换&a…

数据结构- 顺序表-单链表-双链表 --【求个关注!】

文章目录 一 顺序表代码&#xff1a; 二 链表单链表双向链表 一 顺序表 顺序表是线性表的一种 所谓线性表指一串数据的组织存储在逻辑上是线性的&#xff0c;而在物理上不一定是线性的 顺序表的底层实现是数组&#xff0c;其由一群数据类型相同的元素组成&#xff0c;其在逻辑…

WEB 前端优化--一起学习吧之架构

Web前端优化是一个综合性的过程&#xff0c;需要从多个方面入手。通过不断优化和改进&#xff0c;可以提高网站的性能和用户体验&#xff0c;吸引更多用户访问和留存。 一、关键步骤和策略 压缩和优化资源&#xff1a; 压缩HTML、CSS、JavaScript等文件&#xff0c;减少文件大…