继承初级入门复习

news2024/10/5 13:49:08

注意:保护和私有在类中没有区别,但是在继承中有区别,private在继承的子类不可见,protect在继承的子类可见 

记忆方法:先看基类的修饰符是private,那都是不可见的。如果不是,那就用继承的修饰和基类的修饰比较谁小取谁----->public>protect>private

#include <iostream>
using namespace std;
class Person
{
protected:
	string _name;
	string _sex;
	int _age;
};
class Student :public Person
{
public:
	int _No;
};
int main()
{
	Person p;
	Student s;

	//子类和父类之间赋值兼容规则
	//1、子类对象可以赋值父类对象/指针/引用
	p = s;
	Person* p1 = &s;
	Person& p1 =  s;
	return 0;
}

注意:父类不可以赋值给子类,有一种情况就是父类指向子类,再赋值给另一个子类

不同作用域(隐藏(重定义))

class Person
{
protected:
	string _sex;
	int _age=111;
};
class Student :public Person
{
public:
	void f()
	{
		cout << _age << endl;
	}
public:
	int _age=99;
};
int main()
{
	Student s;
	s.f();
}

面向对象的三大特性

封装(类的封装)、继承、多态

实际上面向对象不只三大特性

派生类的默认成员函数

注意:如果是自定义类型就会调用自己的默认构造函数(struct  class)

如何在子类中初始化父类部分呢?

class Person
{
public:
	Person(const char* name)
		:_name(name)
	{

		cout << "Person()" << _name<< endl;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
private:
	string _name;
};
class Student : public Person
{
public:
	Student(const char* name,int age)//所以要加const这个块空间是只读
		:Person(name),
		_stuid(age)

	{

	}
protected:
	int _stuid;
};
int main()
{
	Student a("joker",1);//传入常量字符串
	return 0;
}

父类继承部分一定要调用父类去完成,如果不显示调用的话也会自动调用父类的构造函数

假设1:

默认构造函数包括无参、全缺省、系统默认生成的

如果是系统调用的默认构造函数内置类型是不会处理的,只有自定义类型才会处理

假设2:

如何完成子类拷贝构造呢?

什么是拷贝构造?

1、拷贝构造的参数只有一个,该参数的类型是对象类型的引用,为什么要引用?因为防止无限的调用拷贝构造去递归,一般用const修饰

2、要区分深拷贝和浅拷贝的问题,系统默认生成的拷贝函数的内存存储方式是字节序存储所以要注意成员变量类型的,防止释放两次同一个空间

复习完成

class Person
{
public:
	Person(const char*name="")
		:_name(name)
	{
		cout <<_name << "Person()" << endl;
	}
	Person(const Person& p1)
		:_name(p1._name)
	{
		cout << "Person(const Person& p1)" << endl;
	}
private:
	string _name;
};
class Student :Person
{
public:
	Student(const char*name,int id)
		:Person(name),//要以父类名()的方式调用父类构造函数初始化子类中的父类部分
		_stuid(id)
	{
		cout << "student()" << endl;
	}
	Student(const Student& s1)
		:Person(s1)//要以父类名()的方式调用父类的拷贝构造函数  复制子类中的父类部分
	{
		_stuid = s1._stuid;
		cout << "Student(const Student& s1)" << endl;
	}
private:
	int _stuid;
};

int main()
{
	//Person p1("aaa");
	Student s1("bbb",9);//初始化
	Student s2(s1);//拷贝构造
	//Student s3 ("gg",30);//初始化
	//s1 = s3;//s1.operator=(s3)

	return 0;
}

注意:只有显示的用父类(子类对象)的显示调用方式才可以调用父类的拷贝构造,系统不会自动调用父类的拷贝构造这个和初始化构造不一样

子类的operator=:

class Person
{
public:
//初始化
	Person(const char*name="")
		:_name(name)
	{
		cout <<_name << "Person()" << endl;
	}
//拷贝
	Person(const Person& p1)
		:_name(p1._name)
	{
		cout << "Person(const Person& p1)" << endl;
	}
//父类的operator=函数
	Person& operator=(const Person& s1)
	{
		if (this != &s1)
		{
			_name = s1._name;
			cout << "Person& operator=(const Person& s1)" << endl;
		}
		return *this;
	}
private:
	string _name;//string(const char*s1)
};
class Student :Person
{
public:
	Student(const char*name,int id)
		:Person(name),
		_stuid(id)
	{
		cout << "student()" << endl;
	}
	Student(const Student& s1)
		:Person(s1)//要以父类名()的方式调用父类的拷贝初始化子类中的父类部分
	{
		_stuid = s1._stuid;
		cout << "Student(const Student& s1)" << endl;
	}
//operator=
	Student& operator=(const Student& s1)
	{
		if (this != &s1)//防止赋值时,两个对象时一个对象,浪费时间
		{
			Person::operator=(s1);//显示调用父类的operator=,将父类的部分赋值
			_stuid = s1._stuid;
			cout << "Student& operator=(const Student& s1)" << endl;
		}
		return *this;//返回子类的对象
	}
private:
	int _stuid;
};

int main()
{
	//Person p1("aaa");
	Student s1("bbb",9);//初始化
	Student s2(s1);//拷贝构造
	//Student s3 ("gg",30);//初始化
	//s1 = s3;//s1.operator=(s3)

	return 0;
}

子类的析构函数 

注意:

1、子类的初始化时先父后子,所以析构就是先子后父,类似于stack

2、虽然父类的析构和子类的析构名字不相同,但是子类和父类的析构会被统一的处理成destructor构成隐藏

3、系统已经实现好当子类析构完成了,就会自动调用父类的析构

面试题

如何构建一个不可以继承的类

#include <iostream>
using namespace std;

class A
{
private:   //将父类的初始化私有化
   A()
   {
   }
}
class B:public A
{
  
}
int main()
{
   B b;//初始化不了对象
   return 0;
}

 友元关系不能被继承(了解就好)

会报错,要是想用只可以手动添加

静态成员在父类中被继承会发生什么?


class Person
{
public:
	Person()
	{
		_count++;
	}

public:
	string _name;
	static int _count;
};

int Person::_count = 0;//静态成员必须初始化

class Student:public Person
{
public:
private:
	int id;
};

int main()
{
	Person p1;
	Student s1; 
	p1._name = "jack";
	s1._name = "rose";
	p1._count = 1;
	s1._count = 2;
	cout << Person::_count << endl;//静态成员属于对象也属于类所以可以Person::_count
	return 0;
}

static 成员属于对象也是属于类的,子类继承下来的静态成员和父类是一样的它们调用同一个静态成员变量

多继承

什么叫单继承:只有一个父类

所以多继承:就是有多个父类

多继承会造成很多问题的,所以后来的语言是没有多继承的,多继承是一个大坑

为什么是大坑?

因为多继承的一种特殊的继承菱形继承,会造成数据冗余和二义性

class Person
{
public:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _num; //学号
};
class Teacher : public Person
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";//菱形继承会导致二义性不知道是那个的_name
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

菱形继承:

菱形继承原理

experiment:

class A
{
public:
	int _a;
};
class B:public A
{
public:
	int _b;
};
class C :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;
}

进入调试看内存

这是内存模型,存在数据冗余和二义性

但是我们要看虚继承的内存模型

using namespace std;
class A
{
public:
	int _a;
};
class B:virtual public A//添加了virtual
{
public:
	int _b;
};
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;
}

进入调试内存

接下来看看这些是什么?

好像是地址

再次进入内存

存在偏移量,存放偏移量的地方又叫虚基表

为什么要设计出来呢?

因为菱形继承会出现数据冗余和二义性,所以virtual继承是为了解决二义性和数据冗余

菱形继承:

虚继承:

为什么虚继承会比菱形继承还大呢?

是因为多了两个指针 

我相信很多同学会问不是解决数据冗余吗?为什么越来越大了

因为基类太小了

菱形继承:

虚继承:

少了一倍

这就是虚继承的基本概念

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

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

相关文章

变量命名的艺术:让你的代码更具可读性

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;为何变量命名如此重要&#xff1f; 二、变量命名的基本规则 1. 避免数…

NL6621 实现获取天气情况

一、主要完成的工作 1、建立TASK INT32 main(VOID) {/* system Init */SystemInit();OSTaskCreate(TestAppMain, NULL, &sAppStartTaskStack[NST_APP_START_TASK_STK_SIZE -1], NST_APP_TASK_START_PRIO); OSStart();return 1; } 2、application test task VOID TestAp…

弱监督语义分割-对CAM的生成过程进行改进3

三、擦除图像高响应部分以获取更多的分割领域 ECS-Net: Improving Weakly Supervised Semantic Segmentation by Using Connections Between Class Activation Maps&#xff08;ICCV,2021&#xff09; 1.引言 我们首先从图像中擦除高响应区域&#xff0c;并生成这些擦除图像…

数据防泄露解决方案分享

在当今高度数字化和互联的商业环境中&#xff0c;数据防泄密已成为企业保护财产、维护客户隐私和遵守合规要求的重要一环。数据防泄密不仅关乎企业的经济利益&#xff0c;更涉及用户个人信息安全、商业机密保护以及国家安全等核心问题。能做好数据防泄露&#xff0c;对于提升企…

【简单介绍下爬山算法】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

AIGC:AI整活!万物皆可建筑设计

在过去的一年里 AI设计爆火 各行业纷纷将之用于工作中 同时不少网友也在借助它整活 万物皆可设计 甲方骂我方案像屎一样 于是我就回馈他屎一样的方案 他有点惊喜&#xff0c;但是没话 不是吧&#xff0c;随便找了个充电头图片 也能生成建筑设计&#xff01;这都能行 鸟…

【EasyX】快速入门——消息处理,音频

1.消息处理 我们先看看什么是消息 1.1.获取消息 想要获取消息,就必须学会getmessage函数 1.1.1.getmessage函数 有两个重载版本,它们的作用是一样的 参数filter可以筛选我们需要的消息类型 我们看看参数filter的取值 当然我们可以使用位运算组合这些值 例如,我们…

React-router 最佳实践

使用的是 BrowserRouter&#xff0c;Routes 和 Route&#xff0c;这是 react-router-dom v5 和 v6 都支持的 API。这种方式的优点是路由配置和应用的其它部分是紧密集成的&#xff0c;这使得路由配置更加直观和易于理解 // router/index.js import { BrowserRouter as Router,…

使用 Docker 部署 Jenkins 并设置初始管理员密码

使用 Docker 部署 Jenkins 并设置初始管理员密码 每一次开始&#xff0c;我都特别的认真与胆怯&#xff0c;是因为我期待结局&#xff0c;也能够不会那么粗糙&#xff0c;不会让我失望&#xff0c;所以&#xff0c;就多了些思考&#xff0c;多了些拘束&#xff0c;所以&#xf…

基于YOLO系列算法(YOLOv5、YOLOv6、YOLOv8以及YOLOv9)和Streamlit框架的行人头盔检测系统

摘要 本文基于最新的基于深度学习的目标检测算法 (YOLOv5、YOLOv6、YOLOv8)以及YOLOv9) 对头盔数据集进行训练与验证&#xff0c;得到了最好的模型权重文件。使用Streamlit框架来搭建交互式Web应用界面&#xff0c;可以在网页端实现模型对图像、视频和实时摄像头的目标检测功能…

Error:(6, 43) java: 程序包org.springframework.data.redis.core不存在

目录 一、在做SpringBoot整合Redis的项目时&#xff0c;报错&#xff1a; 二、尝试 三、解决办法 一、在做SpringBoot整合Redis的项目时&#xff0c;报错&#xff1a; 二、尝试 给依赖加版本号&#xff0c;并且把版本换了个遍&#xff0c;也不行&#xff0c;也去update过ma…

基于springboot+vue的在线考试系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

web自动化之PO模式

PO模式 1、为什么需要PO思想&#xff1f; 首先我们观察和思考一下&#xff0c;目前我们写的作业脚本的问题&#xff1a; 元素定位和操作动 作写到一起了&#xff0c;这就就会用导致一个问题&#xff1a; UI的页面元素比较容易变化的&#xff0c;所以元素定位和脚本操作写到一…

【Sync FIFO介绍及基于Verilog的实现】

Sync FIFO介绍及实现 1 Intro2 Achieve2.1 DFD2.2 Intf2.3 Module 本篇博客介绍无论是编码过程中经常用到的逻辑–FIFO&#xff1b;该FIFO是基于单时钟下的同步FIFO&#xff1b; FiFO分类&#xff1a;同步FiFO VS 异步FiFO&#xff1b; 1 Intro FIFO可以自己实现&#xff0c;但…

如何安全地进行隔离网数据导出,提升文件流转效率?

隔离网&#xff08;也称为隔离区或DMZ&#xff0c;即Demilitarized Zone&#xff09;是一种网络安全措施&#xff0c;用于将内部网络与外部网络&#xff08;如互联网&#xff09;隔离开来&#xff0c;以减少安全风险。隔离网数据导出通常需要采取一些特殊的安全措施来确保数据的…

pod介绍之 容器分类与重启策略

目录 一 pod 基础概念介绍 1&#xff0c;pod 是什么 2&#xff0c;Pod使用方式 3&#xff0c;如何解决一个pod 多容器通信 4&#xff0c;pod 组成 5&#xff0c; k8s 中的 pod 二 pause容器 1&#xff0c;pause容器 是什么 2&#xff0c;pause容器作用 3&#xff…

【嵌入式Linux】Cmake、makefile、Cmakelist

记录嵌入式 linux环境下的编译方式 测试之前确保你的 Ubuntu 机器上安装了Gcc和cmake 1. 编译有以下几种方式 在 Linux系统下&#xff0c;编译一个 .c文件可以有以下几种方式&#xff1a; 直接用 Gcc 编译器编译为可执行文件编写Makefile文件&#xff0c;使用 make 指令&…

[LEECODE每日一题]找出最具竞争力的子序列

好久没有更新CSDN了,这段时间学业压力比较忙所以没有时间写,今天有时间来看看LEECODE的每日一题,碰巧刷到了这样一道题; 题目给的很清楚,既输入一个序列要求给定一个子序列长度,让其输出为一个最有"竞争力"的序列,说白了就是在所有子序列比较中,处于靠前位置的元素要…

Kafka之【生产消息】

消息&#xff08;Record&#xff09; 在kafka中传递的数据我们称之为消息&#xff08;message&#xff09;或记录(record)&#xff0c;所以Kafka发送数据前&#xff0c;需要将待发送的数据封装为指定的数据模型&#xff1a; 相关属性必须在构建数据模型时指定&#xff0c;其中…

第2天 搭建安全拓展_小迪网络安全笔记

1.常见搭建平台脚本使用: 例如 phpstudy IIS Nginx(俗称中间件): 什么是中间件: 中间件是介于应用系统和系统软件之间的一类软件&#xff0c;它使用系统软件所提供的基础服务&#xff08;功能&#xff09;&#xff0c;衔接网络上应用系统的各个部分或不同的应用&#…