C++初阶--继承

news2024/11/27 16:25:08

目录

继承的概念

继承定义

继承基类成员访问方式

基类和派生类对象的赋值转换

继承中的作用域

派生类的默认成员函数 

友元关系不能继承

基类static成员

菱形继承与菱形虚拟继承

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

继承和组合


继承的概念

        继承是类层次的复用。

继承定义

//     派生类   继承方式  基类
struct Student : public Person
{

};

继承的使用

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "perter";
	int _age = 18;

private:
	int _aa;
};

//继承后父类的成员(成员函数+成员变量)都会变成子类的一部分
struct Student :public Person
{
public:
	void f()
	{
		//_aa = 1 // 不可见
	}
protected:
	int _stuid;
};

class Teacher :public Person
{
protected:
	int _jobid;
};

继承基类成员访问方式

public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

总结:

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

2.如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。

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

基类和派生类对象的赋值转换

class Person
{
protected:
	string _name; 
	string _sex;  
public:
	void f()
	{
		_age = 1;
	}
private:
	int	_age;	
};

class Student : public Person
{
public:
	int _No; 
};

int main()
{
	Person p;
	Student s;

	 //父类 = 子类    赋值兼容 -> 切割  切片
	 //这里不存在类型转换,是语法天然支持行为
	p = s;
	Person* ptr = &s;
	ptr->f();
	//ptr->_age = 1;
	Person& ref = s;

	//子类 = 父类
	//s = (Student)p;        // 不行
	Student* pptr = (Student*)&p;
	Student& rref = (Student&)p;
	 //很危险的,存在越界访问的风险
	 //pptr->_No = 1;

	return 0;
}

继承中的作用域

1 . 在继承体系中基类和派生类都有独立的作用域。
2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
3. 需要注意的是如果是 成员函数的隐藏 ,只需要 函数名相同 就构成隐藏。
//A 和 B的func构成函数隐藏
class A
{
public:
	void fun()
	{
		cout << "func()" << endl;
	}
};

class B :public A
{
public:
	void fun(int i)
	{
		cout << "func(int i)->" << i << endl;
	}
};

void Test()
{
	B b;
	b.fun(1); 
	//b.fun()   //被隐藏了,所以调不动
	b.A::fun();
}

派生类的默认成员函数 

1. 派生类的 构造函数 必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认
的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的 拷贝构造函数 调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的 operator= 要调用基类的operator=完成基类的复制。
4. 派生类的 析构函数 会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
7. 有些场景析构函数需要构成重写,重写的条件之一是函数名相同,编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
class Person
{
public:
	Person(const char* name = " hh")          //没有默认构造函数的话派生类就要写了
		:_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;
		//delete[] _ptr;
	}

protected:
	string _name;
	//int* _ptr = new int[10];
};

class Student : public Person
{
public:
	Student(const char* name = "张三", int num = 1)
		:Person(name)
		, _num(num)
	{}

	//s2(s1)
	Student(const Student& s)
		:Person(s)
		, _num(s._num)
	{}

	//s2 = s1
	Student& operator=(const Student& s)
	{
		if (this != &s)
		{
			Person::operator = (s);

			_num = s._num;
		}

		return *this;
	}

	//析构函数名字会被统一处理成destructor()
	//所以子类的析构函数跟父类的析构函数就构成隐藏
	~Student()
	{
		//Person::~Person();
		//delete[] _ptr;
	}
	//子类析构函数结束时,会自动调用父类的析构函数
	//不需要显示调用父类析构函数
	//这样才能保证先析构子类成员,再析构父类成员

protected:
	int _num = 1;
	//string _s = "hellllllllllllllllllllllllllo";
	//int* _ptr = new int[10];
};

//不写,编译器默认生成的派生的构造、析构和operator=
//父类继承下来的:调用父类默认构造和析构、operator= 处理     自己的:跟普通类一样

// 什么情况下自己写?
// 1、父类没有默认构造,需要我们自己显示写构造
// 2、如果子类有资源需要释放,就需要自己显示写析构
// 3、如果子类存在浅拷贝问题,就需要自己实现拷贝构造和赋值解决浅拷贝问题

// 如果我们要自己写怎么办?如何写?
// 父类成员调用父类的对应构造、拷贝构造、operator=和析构处理
// 自己成员按普通类处理。

int main()
{
	Student s1;
	Student s2(s1);
	//Student s3("jack", 18);

	//s1 = s3;

	return 0;
}

友元关系不能继承

class Person
{
public:
 friend void Display(const Person& p, const Student& s);
protected:
 string _name; // 姓名
};
class Student : public Person
{
protected:
 int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
 cout << p._name << endl;
 cout << s._stuNum << endl;
}
void main()
{
 Person p;
 Student s;
 Display(p, s);
}

基类static成员

        基类定义了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;
};

class Graduate :public Student
{
protected:
	string _seminarCourse;
};

int main()
{
	Person p;
	Student s;
	Graduate g;

	cout << Person::_count << endl;
	cout << Student::_count << endl;
	cout << Graduate::_count << endl;

	return 0;
} 

菱形继承与菱形虚拟继承

单继承:一个子类只有一个直接父类

多继承:一个子类有两个或以上直接父类

菱形继承:是多继承的一种特殊情况

菱形继承有数据冗余和二义性的问题。
class Person
{
public:
	string _name;
	int _a[1000];
};

class Student :public Person
{
public:
	int _num;
};

class Teacher :public Person
{
public:
	int _id;
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse;  
};

int main()
{
	//二义性、数据冗余
	Assistant a;
	a._id = 1;
	a._num = 2;
	a.Student::_name = "小张";
	a.Teacher::_name = "张老师";

	cout << sizeof(a) << endl;

	return 0;
}
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。
//虚继承解决数据冗余和二义性
class Person
{
public:
	string _name;
	int _a[1000];
};

class Student : virtual public Person
{
public:
	int _num;
};

class Teacher : virtual public Person
{
public:
	int _id;
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse;
};

int main()
{
	//二义性、数据冗余
	Assistant a;
	a._id = 1;
	a._num = 2;
	a.Student::_name = "小张";
	a.Teacher::_name = "张老师";

	cout << sizeof(a) << endl;

	return 0;
}

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

以下代码为例

class A
{
public:
	int _a;
};

class B : virtual public A
{
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;
	d._a = 0;

	return 0;
}

        A一般叫虚基类,在D里面,A放到一个公共位置,那么BC有时需要找A,通过B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。

继承和组合

1.public继承是一种is-a的关系。每个派生类对象都是一个基类对象。组合是一种has-a的关系。B组合了A,那么每个B对象中都有一个A对象。
2.优先使用对象组合。
3.继承是根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse),一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
4.对象组合是继承之外的另一种复用选择。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。
5.优先使用对象组合有助于保持每个类被封装。组合的耦合度低,代码维护性好。
6.要实现多态,也必须要继承。

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

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

相关文章

Golang 泛型学习

Golang 泛型 今天来学习下Golang中泛型的基础知识。使用泛型&#xff0c;开发者可以声明一个函数来适应不同类型的参数&#xff0c;避免由于参数类型不一致而声明多个处理逻辑类似的函数。在本教程中&#xff0c;将声明两个简单的非泛型函数&#xff0c;然后在单个泛型函数中实…

这些实体店直播必备技巧,新手直接套用就能火!

随着直播的受众越来越广、门槛越来越低&#xff0c;入局服装直播的实体店越来越多。对于服装厂商来说&#xff0c;服装产业链越靠下游毛利率越高&#xff0c;品牌商和销售商利润远高于加工生产商&#xff0c;约在40-50%&#xff0c;而服装制造商的毛利率仅在15%左右。而对于本土…

JDK8 新特性之收集Stream流中的结果

目录 一&#xff1a;Stream流中的结果到集合中 二&#xff1a;Stream流中的结果到数组中 三&#xff1a;对流中数据进行聚合计算 四&#xff1a;对流中数据进行分组 五&#xff1a;对流中数据进行多级分组 六&#xff1a;对流中数据进行分区 七&#xff1a;对流中数据进行拼接…

8.Java循环高级综合练习-无限循环和跳转控制语句,逢七过,平方根,判断是否为质数,猜数字小游戏

文章目录前言一、无限循环1.这三种循环中哪一种无限循环是最常用的呢?2.注意事项:二、跳转控制语句三、逢七过四、平方根五、判断该整数是否为一个质数六、猜数字小游戏保底机制总结前言 一、无限循环 1.这三种循环中哪一种无限循环是最常用的呢? 当然是右上角的while循环啦…

【若依】若依字典管理页面中列表按钮功能的实现

0. 功能实现描述 1. 代码实现 ScStfController.java /*** 查询员工证书* param stfId* param modelMap* return*/ RequiresPermissions("sc:stf:cert") GetMapping("/cert/{stfId}") public String detail(PathVariable("stfId") Long stfId, …

结构型模式-组合模式

1.概述 对于这个图片肯定会非常熟悉&#xff0c;上图我们可以看做是一个文件系统&#xff0c;对于这样的结构我们称之为树形结构。在树形结构中可以通过调用某个方法来遍历整个树&#xff0c;当我们找到某个叶子节点后&#xff0c;就可以对叶子节点进行相关的操作。可以将这颗树…

谷粒学院——Day19【项目部署】

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

Java中的hashCode,真的很容易弄懂

写这篇文章是因为在看hashMap源码时遇到有什么hashcode值&#xff0c;然后就去查&#xff0c;脑袋里面是有印象的&#xff0c;不就是在Object中有equals和hashcode方法嘛&#xff0c;这在学java基础的时候就遇到过&#xff0c;不过那时候无所谓&#xff0c;囫囵吞枣&#xff0c…

三、python基础语法进阶篇(黑马程序猿-python学习记录)

黑马程序猿的python学习视频&#xff1a;https://www.bilibili.com/video/BV1qW4y1a7fU/ 目录 一、文件操作 一、 文件的读取 1. 打开文件open() 2. 读取文件10个字节read(10) 3. 读取文件全部信息read() 4. 读取文件readLines() 5. 读取文件readLine() 6. for循环读取…

Nginx与LUA(7)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客。值此新春佳节&#xff0c;我给您拜年啦&#xff5e;祝您在新的一年中所求皆所愿&#xff0c;所行皆坦途&#xff0c;展宏“兔”&#xff0c;有钱“兔”&#xff0c;多喜乐&#xff0c;常安宁&#xff01;软件开发中&…

使用小程序+网页简易实现多客户端实时弹幕

此文主要通过小程序网页模拟多客户端通过轮询、WebSockets、订阅推送等方式简易实现实时弹幕。 实现流程1、服务端1.1、创建项目2.2、接口定义2、客户端2.1、小程序端2.2、web端3、实现方式3.1、轮询3.2、WebSocket3.3、订阅推送实现流程 1、服务端 1.1、创建项目 打开Visual…

【docker概念和实践 5】容器命令和案例(1)

一、说明 docker的四个要素是&#xff1a;本地的Docker-engine、网上&#xff08;本地&#xff09;的仓库、镜像images、容器&#xff1b;初学者必须了解这是个概念的关系。但是&#xff0c;真正重要的概念是容器&#xff0c;因为&#xff0c;只有掌握了容器&#xff0c;才能具…

SpringBoot整合SSM

添加pom依赖 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.18</version><scope>provided</scope></dependency><dependency><groupId>org.mybati…

macOS Monterey 12.6.3 (21G419) Boot ISO 原版可引导镜像

macOS Monterey 12.6&#xff0c;皆为安全更新&#xff0c;不再赘述。 macOS Monterey 12.6&#xff0c;发布于 2022 年 9 月 12 日&#xff08;北京时间今日凌晨&#xff09;&#xff0c;本次为安全更新。 今日&#xff08;2022-07-21&#xff09;凌晨&#xff0c;Apple 终于…

【Hadoop】HDFS高可用与高扩展原理分析(HA架构与Federation机制)

文章目录一、HDFS的高可用性&#xff08;HA架构&#xff09;二、HDFS的高扩展性&#xff08;Federation机制&#xff09;三、HA架构 Federation机制一、HDFS的高可用性&#xff08;HA架构&#xff09; 为保证HDFS的高可用性&#xff0c;即当NameNode节点机器出现故障而导致宕机…

【操作系统】—— Windows常用快捷键(带你快速了解)

&#x1f4dc; “作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴。 &#x1f341; 操作系统【带你快速了解】对于电脑来说&#xff0c;如果说…

【JavaEE】定时器的简单实现

目录 定时器 实现定时器 描述任务 保存任务 扫描任务 执行任务 定时器 在实现定时器之前&#xff0c;先来简单的了解一下什么是定时器。 定时器是软件开发中一个重要的组件。比如到了什么时候&#xff0c;干一件什么样的事情&#xff1b;多少秒之后干什么。本篇文章介绍…

活动星投票最美养生师展网络评选微信的投票方式线上免费投票

“最美养生师”网络评选投票_用户同什么方法挑选投票小程序_最好的投票小程序用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务&…

Hive函数大全–完整版(三)

官网参考地址&#xff1a; 官网UDF - Apache Hive 1. 基本数据类型 2. 基础运算符与函数 SQL结果A IS NULL 空A IS NOT NULL 非空 A LIKE B 模糊匹配A RLIKE B 正则表达式匹配A REGEXP B 正则表达式匹配 3. 类型转换 cast(expr as <type>)…

园区网典型组网架构及案例实践

什么是园区网园区网络是限定区域内&#xff0c;连接人与物的局域网络&#xff1b;园区网络通常只有一个管理主体&#xff1b;如果有多个管理主体&#xff0c;通常被认为为多个园区网络。园区网络典型架构小型园区网络典型架构小型园区网络应用于接入用户数量较少的场景&#xf…