【C++】继承的基本特性(定义,赋值转换,友元,静态成员,虚拟继承,默认成员函数,作用域)

文章目录

  • 一、继承的定义
    • 1.定义格式
    • 2.继承基类成员访问方式的变化
  • 二、基类和派生类对象赋值转换
  • 三、继承的作用域
    • 1. 在继承体系中基类和派生类都有独立的作用域。
    • 2.子类和父类中有同名成员
    • 3.成员函数的隐藏
    • 4.注意在实际中在继承体系里面最好不要定义同名的成员。
  • 四、派生类的默认成员函数
  • 五、继承与友元
  • 六、继承与静态成员
  • 七、菱形虚拟继承
    • 1.菱形继承
    • 2.虚拟继承
    • 3.菱形虚拟继承原理:
      • 1.菱形继承的内存分布
      • 2.虚拟继承内存分布


一、继承的定义

它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。

1.定义格式

在这里插入图片描述
Person是父类,也称作基类。Student是子类,也称作派生类

2.继承基类成员访问方式的变化

在这里插入图片描述
总结:

  1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在里面还是类外面都不能去访问它。
    在这里插入图片描述

  2. 基类private成员在派生类中是不能被访问, 如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。 可以看出保护成员限定符是因继承才出现的。

  3. 基类的私有成员在子类都是不可见, 基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。

  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

  5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承

二、基类和派生类对象赋值转换

1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。
2.基类对象不能赋值给派生类对象。
在这里插入图片描述
在这里插入图片描述

三、继承的作用域

1. 在继承体系中基类和派生类都有独立的作用域。

2.子类和父类中有同名成员

子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
在这里插入图片描述

3.成员函数的隐藏

需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
在这里插入图片描述

4.注意在实际中在继承体系里面最好不要定义同名的成员。

四、派生类的默认成员函数

基类(父类):

class Person {
public:Person(const char*name):_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 = 0)//派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。:Person(name),_id(id){}Student(const Student& s) // 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。:Person(s)//切割,_id(s._id){}Student& operator=(const Student& s) {//派生类的operator = 必须要调用基类的operator = 完成基类的复制if (this != &s) {Person::operator=(s);//发生切割_id = s._id;}return *this;}~Student(){// 析构函数的函数名被// 特殊处理了,统一处理成destructor// 显示调用父类析构,无法保证先子后父// 所以子类析构函数完成就,自动调用父类析构,这样就保证了先子后父//析构函数:先析构子类后析构父类//构造函数:先构造父类后构造子类}protected:int _id;
};

运行结果:
在这里插入图片描述
总结:

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
    保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 派生类对象析构清理先调用派生类析构再调基类的析构。

在这里插入图片描述

五、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
在这里插入图片描述
想要正常运行需要派生类也加上友元才可以

六、继承与静态成员

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

在这里插入图片描述

七、菱形虚拟继承

1.菱形继承

在这里插入图片描述
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。
在这里插入图片描述

2.虚拟继承

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
在这里插入图片描述

3.菱形虚拟继承原理:

class A
{
public:int _a;
};
// class B : public A
class B : virtual public A
{
public:int _b;
};
// class C : public A
class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

1.菱形继承的内存分布

下图是菱形继承的内存对象成员模型:这里可以看到数据冗余
在这里插入图片描述

2.虚拟继承内存分布

下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity Shader编辑器工具类ShaderUtil 常用函数和用法

Unity Shader编辑器工具类ShaderUtil 常用函数和用法 Unity的Shader编辑器工具类ShaderUtil提供了一系列函数&#xff0c;用于编译、导入和管理着色器。本文将介绍ShaderUtil类中的常用函数和用法。 编译和导入函数 CompileShader 函数签名&#xff1a;public static bool C…

Apache Kafka Learning

目录 一、Kafka 1、Message Queue是什么&#xff1f; 2、Kafka 基础架构 3、Kafka安装 二、Maven项目测试 1、Topic API 2、生产者&消费者 一、Kafka Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式…

openCV图像读取和显示

文章目录 一、imread二、namedWindow三、imshow #include <opencv2/opencv.hpp> #include <iostream>using namespace std; using namespace cv;int main(int argc,char** argv) {cv::Mat img imread("./sun.png"); //3通道 24位if (img.empty()) {std:…

Typescript中的元组与数组的区别

Typescript中的元组与数组的区别 元组可以应用在经纬度这样明确固定长度和类型的场景下 //元组和数组类似&#xff0c;但是类型注解时会不一样//元组赋值的类型、位置、个数需要和定义的类型、位置、个数完全一致&#xff0c;不然会报错。 // 数组 某个位置的值可以是注解中的…

理解 CSS 中的 Containing Block

前言 在开始本文之前先来看一个例子&#xff0c;下面一段简单的 html 代码&#xff0c;布局很简单&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"w…

2023年人工智能技术与智慧城市发展白皮书

人工智能与智慧城市是当前热门的话题和概念&#xff0c;通过将人工智能技术应用在城市管理和服务中&#xff0c;利用自动化、智能化和数据化的方式提高城市运行效率和人民生活质量&#xff0c;最终实现城市发展的智慧化&#xff0c;提升城市居民的幸福感。 AI技术在城市中的应…

PHP客服系统聊天页面-thinkphp加载页面源码解释

PHP workerman客服系统加载聊天页面的代码逻辑流程&#xff0c;可以进行参考。如果想要二开修改的&#xff0c;可以根据这个流程来修改。 thinkphp的router部分 Route::get(kefu/:u/:f?, index/index/chat);查看控制器加载页面逻辑 application/index/controller/Index.php pu…

Vue插槽 、自定义指令、render函数、过滤器和插件

目录 插槽 自定义指令 directive 全局注册 局部注册 钩子函数 render渲染函数 过滤器 插件 plugin 插槽 普通插槽&#xff0c;具名插槽&#xff0c;作用域插槽 插槽允许我们在调用子组件的时候为子组件传递模板。 <slot> 元素作为承载分发内容的出口。 一个不带…

kubernetes之Ingress

一、背景 Ingress是k8s中实现7层负载的实现方式&#xff0c;是公开集群外部流量到集群内服务的HTTP和HTTPS路由 二、Ingress基础 通常Ingress实现由Ingress 控制器和Ingress组成&#xff0c;Ingress控制器负责具体实现反向代理及负载均衡&#xff0c;Ingress负责定义匹配规则和…

Kubernetes高可用集群二进制部署(四)部署kubectl和kube-controller-manager、kube-scheduler

Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署&#xff08;一&#xff09;主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署&#xff08;二&#xff09;ETCD集群部署 Kubernetes高可用集群二进制部署&#xff08;三&#xff09;部署…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--搭建Vue 前端工程[一]

文章目录 SSM--搭建Vue 前端工程--项目基础界面实现功能01-搭建Vue 前端工程需求分析/图解代码实现搭建Vue 前端工程下载node.js LTS 并安装: node.js 的npm创建Vue 项目使用idea 打开ssm_vue 项目, 并配置项目启动 Vue3 项目目录结构梳理Vue3 项目结构介绍 配置Vue 服务端口El…

C++封装思想之二:友元机制和运算符重载(1W字详解)

目录 友元机制和运算符重载 友元机制 友元函数 友元的作用 友元类 前置声明 友元类的注意事项 友元成员函数&#xff08;类的某个成员函数 作为另一个类的友元&#xff09; 运算符重载 运算符重载的作用 运算符重载的注意事项 运算符重载的实现 成员函数重载 友…