C++进阶—继承(上)简单特性

news2024/12/23 10:43:41

目录

1.继承的概念及定义

1.1继承的概念

1.2 继承定义

1.2.1定义格式

 1.2.2继承关系和访问限定符

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

2.基类和派生类对象赋值转换

3.继承中的作用域

4.派生类的默认成员函数

5.继承与友元

6. 继承与静态成员


1.继承的概念及定义

1.1继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用

namespace Nenten {
	class Person
	{
	public:
		void Print()
		{
			cout << "name:" << _name << endl;
			cout << "age:" << _age << endl;
		}
	protected:
		string _name = "peter"; // 姓名
		int _age = 18;  // 年龄
	};
	// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
	/*Student和Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可
		以看到变量的复用。调用Print可以看到成员函数的复用。*/

	class Student : public Person
	{
	protected:
		int _stuid; // 学号
	};

	class Teacher : public Person
	{
	protected:
		int _jobid; // 工号
	};

    void test1() {
		Student s;
		Teacher t;
		s.Print();
		t.Print();
	}
		
}

1.2 继承定义

1.2.1定义格式

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类。

 1.2.2继承关系和访问限定符

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

 总结:

  1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它。
  2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
  3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
  5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

2.基类和派生类对象赋值转换

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去
  • 基类对象不能赋值给派生类对象。
  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的
  • 这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。
	class Person
	{
	protected:
		string _name; // 姓名
		string _sex;  // 性别
		int _age; // 年龄
	};
	class Student : public Person
	{
	public:
		int _No; // 学号
	};
	void Test()
	{
		Student sobj;
		// 1.子类对象可以赋值给父类对象/指针/引用
		Person pobj = sobj;
		Person* pp = &sobj;
		Person& rp = sobj;

		//2.基类对象不能赋值给派生类对象
		sobj = pobj;

		// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
		pp = &sobj;
		Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
		ps1->_No = 10;

		pp = &pobj;
		Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
			ps2->_No = 10;
	}

【注意】

  1. 子类对象可以赋值给父类对象/指针/引用
  2. 子类对象到父类对象,不是隐式类型转换
  3. 这里算是一个特殊支持,语法天然支持,也叫切割、切片

3.继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
  4. 注意在实际中在继承体系里面最好不要定义同名的成员。
 //Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆
	class Person
	{
	protected:
		string _name = "小李子"; // 姓名
		int _num = 111;   // 身份证号
	};
	class Student : public Person
	{
	public:
		void Print()
		{
			cout << " 姓名:" << _name << endl;
			cout << " 身份证号:" << Person::_num << endl;
			cout << " 学号:" << _num << endl;
		}
	protected:
		int _num = 999; // 学号
	};
	void Test()
	{
		Student s1;
		s1.Print();
	};
class A
	{
	public:
		void fun()
		{
			cout << "func()" << endl;
		}
	};
	class B : public A
	{
	public:
		void fun(int i)
		{
			A::fun();
			cout << "func(int i)->" << i << endl;
		}
	};
	void Test() {
		B b;
		b.fun(10);
	};

4.派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类 中,这几个成员函数是如何生成的呢?

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

		}
		Student(const char* name, int num)
			: Person(name)
			, _num(num)
		{
			cout << "Student()" << endl;
		}

		Student(const Student& s)
			: Person(s)
			, _num(s._num)
		{   
			cout << "Student(const Student& s)" << endl;
		}
		Student& operator = (const Student& s)
		{
			cout << "Student& operator= (const Student& s)" << endl;
			if (this != &s)
			{
				Person::operator =(s);
				_num = s._num;
			}
			return *this;
		}

		~Student()
		{
			//析构函数不需要显示调用,因为它会自动调用
			//Person::~Person();
			cout << "~Student()" << endl;
		}
	protected:
		int _num; //学号
	};

 

 

5.继承与友元

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

class Student;
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);
}

6. 继承与静态成员

基类定义了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; // 研究科目
	};
	void TestPerson()
	{
		Student s1;
		Student s2;
		Student s3;
		Graduate s4;
		cout << " 人数 :" << Person::_count << endl;
		Student::_count = 0;
		cout << " 人数 :" << Person::_count << endl;
	}

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

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

相关文章

OpenSSL生成SSL证书,受浏览器信任吗?

OpenSSL是用于传输层安全(TLS)协议的开源工具包&#xff0c;OpenSSL生成SSL证书能受到浏览器信任吗&#xff1f;OpenSSL生成SSL证书能不能用于网站HTTPS加密呢&#xff1f; OpenSSL是什么&#xff1f; OpenSSL是基于密码学的用于传输层安全(TLS)协议的开源工具包&#xff0c;可…

【U8+】取消用友U8软件登录界面记住密码功能

【需求描述】 由于用友U8结合远程软件使用&#xff0c; 并且为了简化操作&#xff0c;远程用户建立一个公用账户&#xff0c; 所有的U8用户都使用同一个远程用户登录&#xff0c; 但是各自有U8的账号&#xff0c;登录账套的时候&#xff0c;有操作员记录密码后&#xff0c;别的…

Springboot 核心注解和基本配置解读

目录 1. Springboot 入门与原理 1.1 Springboot 简介 1.1.1 什么是Springboot 1.1.2 Springboot 主要优点 1.2 Springboot 相关注解 1.2.1 元注解 1.2.1.1 Target 1.2.1.2 Retention 1.2.2 Configuration 1.2.3 Import 1.2.3.1 直接注入 1.2.3.2 实现 ImportSelector…

Python基础语法2(超详细举例)

生活就是这样&#xff0c;有的时候即便你尽了最大努力&#xff0c;但依然无法得偿所愿 但是&#xff0c;难道向上攀爬的那条路不是比站在顶峰更令人热血澎湃吗&#xff1f; 文章目录 一、转义符 二、变量的赋值规则 三、数据类型 四、操作符 1.除法 2.幂运算 3.布尔运算…

读营销策划心得

读营销策划心得篇1 过去的一年可算是我工作上另一个转折点&#xff0c;更是一个新的开始。特别是自今年6月份接手营销策划工作&#xff0c;不知不觉&#xff0c;已有半年。回忆这一年的工作经历&#xff0c;有艰辛、有成长、有收获、更有前景。这一年既包含了太多的艰辛与不易&…

Redis【入门篇】---- 初始 Redis

Redis【入门篇】---- 初始 Redis 1. 认识NoSQL1. 结构化与非结构化2. 关联与非关联3. 查询方式4. 事务5. 总结 2. 认识Redis3. 安装Redis1. 依赖库2. 上传安装包并解压3. 启动4. 默认启动5. 指定配置启动6. 开机自启动 4. Redis桌面客户端1. Redis命令行客户端2. 图形化桌面客户…

2023年最新企业网盘排名!一文掌握各大企业网盘优缺点

近年来&#xff0c;企业网盘已经成为一个越来越流行的工具&#xff0c;为企业寻求简化他们的文件协作过程。由于团队成员分散在不同的位置和设备上&#xff0c;网盘提供了一种安全有效的方式来存储、共享和协作文件&#xff0c;为企业提供了一系列的好处&#xff0c;包括&#…

赚钱的底层模式和破局思路

赚钱的逻辑是什么&#xff0c;哪些价值观念的区别&#xff0c;让不同人在赚钱这件事情上产生巨大的差别&#xff1f; 如果从第一性原理出发&#xff0c;个体赚钱有哪些模式&#xff0c;以及如何优化&#xff1f; 一、出卖时间 本质上所有的赚钱方式都是出卖时间&#xff0c;…

车载开发中,蓝牙通信需要学习那些核心技术点?

车载蓝牙通信是指在汽车内部或车辆与外部设备之间使用蓝牙技术进行数据传输和通信。蓝牙5.0是现代蓝牙技术的最新版本&#xff0c;它引入了一系列新功能和改进&#xff0c;提供了更快的数据传输速度、更长的传输距离、更稳定的连接和更低的能耗。 那么车载蓝牙通信需要学习那些…

RabbitMQ实现延时消息的两种方法

RabbitMQ实现延时消息的两种方法 1、死信队列 1.1消息什么时候变为死信(dead-letter) 消息被否定接收&#xff0c;消费者使用basic.reject 或者 basic.nack并且requeue 重回队列属性设为false。消息在队列里得时间超过了该消息设置的过期时间&#xff08;TTL&#xff09;。消…

java 设计模式--创建者模式

参考&#xff1a;Java常见设计模式总结 概念 概念理解一&#xff1a;将复杂对象的创建过程分解在不同的方法中&#xff0c;不同的创建过程组装成不同对象。对象的创建与产品本身分离开&#xff0c;使得对象的创建过程更加清晰。例如&#xff1a;旅游套餐售卖场景。 一个套餐大…

@DateTimeFormat与@JsonFormat不完全解析

目录 前言测试代码DateTimeFormat不加任何注解的情况普通请求JSON请求 JsonFormat普通请求JSON请求 其他方式&#xff08;InitBinder&#xff09;结论源码地址 前言 一直以来对DateTimeFormat与JsonFormat 比较模糊&#xff0c;容易搞忘&#xff0c;今天就做个笔记&#xff0c…

【MySQL 利器之 mysqldump】

文章目录 前言一、mysqldump二、环境三、使用步骤1.服务器与服务器间直接同步2.导出到sql文件3.sql文件导入 总结使用方式 1 服务器间直连方式同步&#xff1a;使用中间SQL 文件方式&#xff1a;datax&#xff1a; 前言 1.随着服务器环境改造&#xff0c;新旧数据库环境更换&a…

微流控压力控制器和微流控注射泵的性能比较

摘要&#xff1a;针对微流控技术中的压力和流量控制&#xff0c;本文介绍了目前常用的两类装置&#xff1a;注射泵和压力泵&#xff0c;重点介绍了这两种装置的性能特点&#xff0c;并对这两种压力控制装置进行了简要的分析对比。分析结论是压力泵将逐渐替代注射泵的应用&#…

Addressable CRC设置详解

设置 Asset Bundle的CRC设置中有三个选项&#xff1a; Disable&#xff1b; Enable,InClude Cached; Enable,Excludeing Cached; 修改后实际改的是这里的选项&#xff1a; Disable 设置为Disable&#xff0c;实际上是将BundledAssetGroupSchema类的UseAssetBundleCrc参数设…

软考:软件工程:软件设计,总体设计,详细设计,耦合内聚流程图,NS图,PAD图,判定树判定图。

软考&#xff1a;软件工程: 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &#xff08;1&#…

【吃透网络安全】2023软考网络管理员考点网络安全(二)网络攻击详解

涉及知识点 黑客的攻击手段介绍&#xff0c;常见的网络攻击&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 后面还有更多续篇希望大家能给个赞哈&#xff0c;这边提供个快捷入口&#xff01; 第一节网络管理员考…

多版本管理node.js

多版本管理node.js 1. 安装2. 配置使用2.1 修改node源2.2 常用命令 在Windows 计算机上管理node.js的多个安装版本。 这是朋友推荐的&#xff0c;就是自己在升级node的时候给搞崩了&#xff0c; 不得不提升效率&#xff0c;于是发现了这个好工具&#xff0c;可以反过来理解&…

金蝶云星空RCE漏洞复现

0x01 产品简介 金蝶云星空是一款云端企业资源管理&#xff08;ERP&#xff09;软件&#xff0c;为企业提供财务管理、供应链管理以及业务流程管理等一体化解决方案。金蝶云星空聚焦多组织&#xff0c;多利润中心的大中型企业&#xff0c;以 “开放、标准、社交”三大特性为数字…

端午安康,节日送祝福

端午节是在中国农历的五月份&#xff0c;今年是&#xff08;公历&#xff09;&#xff16;月22日&#xff0c;它是中国最古老的节日 之一&#xff0c;已经有两千多年的历史。The Duanwu or Dragon Boat Festival, is generally celebrated on the fifth month of the Chinese l…