【C++初识继承】

图片名称

博主首页: 有趣的中国人
 

专栏首页: C++进阶
 


本篇文章主要讲解 继承 的相关内容


目录

1. 继承的概念和定义

1.1 继承的概念

 1.2 继承的定义

1.2.1 继承定义格式

1.2.2 继承方式与访问修饰限定符

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

3. 继承中的作用域

3.1 全局域、局部域、类域、命名空间域

3.2 继承作用域

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

4.1 构造函数和拷贝构造

4.2 赋值重载和析构函数


1. 继承的概念和定义

1.1 继承的概念

在C++中,继承是一种机制,它允许一个类(称为子类或派生类)另一个类(称为父类或基类)获取成员变量和成员函数。子类可以继承父类的属性和行为,并且可以添加自己的属性和行为。这种继承关系可以用来建立类之间的层次结构,从而实现代码的重用和抽象。在C++中,继承有三种类型:公有继承、保护继承和私有继承,它们决定了父类的成员对子类的可见性。

using namespace std;
class Person
{
public:
	void Print()
	{
		cout << "name:" << name << endl;
		cout << "age:" << age << endl;
	}
protected:
	string name = "Jesicca";
	int age = 18;
};

// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
Student和Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可
以看到变量的复用。调用Print可以看到成员函数的复用。

class Student : public Person
{
protected:
	int stu_id;
};

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

int main()
{
	Student st;
	Teacher t;
	st.Print();
	t.Print();
	return 0;
}

 1.2 继承的定义

1.2.1 继承定义格式

1.2.2 继承方式与访问修饰限定符

 总的来说:

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

class Person
{
public:
	void Print()
	{
		cout << "name:" << name << endl;
		cout << "age:" << age << endl;
	}
protected:
	string name = "Jesicca";
// age 为private类型,继承之后无论在派生类内还是类外都不能被访问
private:
	int age = 18;
};

class Student : public Person
{
public:
    // 这里会报错,age不能被访问
	void Stuage()
	{
		cout <<  "Student age:" << age << endl;
	}
protected:
	int stu_id = 20;
};

虽然不能被访问,但是实际上还是被继承了:

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

class Person
{
public:
	void Print()
	{
		cout << "name:" << name << endl;
		cout << "age:" << age << endl;
	}
protected:
	string name = "Jesicca";
    // age 变成了protected类型了
	int age = 18;
};

class Student : public Person
{
public:
    // 再类中能被访问
	void Stuage()
	{
		cout <<  "Student age:" << age << endl;
	}
protected:
	int stu_id = 20;
};

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

4. 在实际运用中一般使用都是public继承几乎很少使用protetced/private继承,也不提倡
使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里
面使用。
 


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

派生类对象可以赋值给基类的对象、基类的指针、基类的引用。这个操作叫做切片,

对于内置类型是直接拷贝,自定义类型调用拷贝构造,且切片过程中会进行赋值兼容不会产生临时变量

必须是基类的指针或者引用指向派生类才是安全的!

例如以下示例:

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

class Student : public Person
{
public:
	void Stuage()
	{
		cout <<  "Student age:" << age << endl;
	}
protected:
	int stu_id = 20;
};
int main()
{
	Student st;
	// 派生类对象给基类对象
	Person p = st;
	// 派生类给基类指针
	Person* pst = &st;
	// 派生类给父类引用
	Person& ref = st;
	ref.name += "j";
	ref.Print();
	pst->name += "k";
	pst->Print();
	return 0;
}

3. 继承中的作用域

3.1 全局域、局部域、类域、命名空间域

先回顾以下C++中其他的域:

全局域、局部域、类域、命名空间域,

其中全局域和局部域会影响生命周期,但是类域和命名空间域不会影响生命周期,再同一个域中不能出现同名的成员变量,也不能出现非重载的两个同名函数

3.2 继承作用域

1. 在父类和派生类中可以定义两个同名的变量,因为父类和派生类是两个不同的作用域

2. 子类和父类如果有同名的变量,子类成员会屏蔽掉父类中同名的成员变量这叫做隐藏或者重定义,可以使用 基类::基类成员 进行显示访问

class Person
{
public:
	void Print()
	{
		cout << "name:" << name << endl;
		cout << "age:" << age << endl;
	}
	string name = "Jesicca";
protected:
	int age = 20;
};

class Student : public Person
{
public:
	void Stuage()
	{
        // 父类同名变量会被屏蔽,构成隐藏
		cout << "Stuage():" << age << endl;
        // 显示访问父类同名成员变量
		cout << "Personage:" << Person::age << endl;
	}
protected:
	int stu_id = 20;
	int age = 19;
};

 看以下这段代码:

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

B中的fun和A中的fun不是构成重载,因为不是在同一作用域
B中的fun和A中的fun构成隐藏成员函数满足函数名相同就构成隐藏。
 


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

默认构造是自己不写,编译器也能自动生成的:构造函数、拷贝构造、析构函数、赋值重载、取地址重载这些都是默认构造(在类和对象的文章中讲过)。

4.1 构造函数和拷贝构造

在派生类中,成员对象大体可以分为三大类:基类中继承下来的、自定义类型、内置类型

对于自定义类型还是调用在身的构造函数,内置类型不做处理。

对于基类中继承下来的成员,必须调用基类的构造函数初始化基类中的部分成员,如果基类中没有默认构造函数,那么必须在派生类的构造函数中显示调用基类的构造函数,不然就会报错。

例如以下的例子:

class Person
{
public:
    // 没有默认构造函数
	Person(const char* name)
		:name(name)
	{}
	void Print()
	{
		cout << "name:" << name << endl;
		//cout << "age:" << age << endl;
	}
protected:
	string name;
	//int age = 20;
};

class Student : public Person
{
public:
    // 由于基类中没有默认生成的构造函数,所以在派生类的构造函数必须显示调用基类的构造函数
	Student(const char* str, const char* name, int stu_id = 20, int age = 19)
        // 这边显示调用,类似于匿名对象的调用,要把基类看成一个整体进行调用
		:Person(name)
		,stu_id(stu_id)
		,age(age)
		,str(str)
	{}
	void Stuage()
	{
		cout << "Stuage():" << age << endl;
		//cout << "Personage:" << Person::age << endl;
	}
protected:
	string str;
	int stu_id;
	int age;
};

对于拷贝构造,派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化,例如:

// 父类
Person(const Person& p)
		:name(p.name)
	{}

// 子类
Student(const Student& st)
		:Person(st)
	{
		str = st.str;
		stu_id = st.stu_id;
		age = st.age;
	}

4.2 赋值重载和析构函数

 派生类的operator=必须要调用基类的operator=完成基类的复制。

Person& operator=(const Person& p)
{
	if (this != &p)
	{
		name = p.name;
	}
	return *this;
}



Student& operator=(const Student& st)
{
	if (this != &st)
	{
        // 这里如果写成operator=(st);会构成隐藏,会无穷递归
		Person::operator=(st);
		str = st.str;
		stu_id = st.stu_id;
		age = st.age;
	}
	return *this;
}

子类析构函数和父类析构函数构成隐藏关系。

派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
保证派生类对象先清理派生类成员再清理基类成员的顺序。

~Person()
{
	cout << "~Person()" << endl;
}

~Student()
{
	cout << "~Student()" << endl;
    // 会构成隐藏,因此如果写要显示写,
    // 但是为了保证析构的顺序是先子后父,编译器会在程序结束时自动调用父类的析构函数,不需要显示写,        
    // 因为在此时可能需要访问父类中的对象,如果析构就访问不了了
	//Person::~Person();
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

读天才与算法:人脑与AI的数学思维笔记05_算法的幻觉

1. 自下而上 1.1. 代码在未来可以自主学习、适应并进行自我改进 1.2. 程序员通过编程教会计算机玩游戏&#xff0c;而计算机却会比教它的人玩得更好&#xff0c;这种输入寡而输出众的事情不大可能实现 1.3. 早在20世纪50年代&#xff0c;计算机科学家们就模拟该过程创造了感…

内存概念理解:RANK,BANK,BURST,INTERLEAVING

背景&#xff1a;死磕内存的bank和rank概念的一天。网上的资料都差不多&#xff0c;还是有些地方没理通顺&#xff0c;有什么内存基础知识的书籍可以推荐吗&#xff1f; 物理RANK的概念 当我们给计算机购买内存条时候&#xff0c;上面显示的1RX8, 2RX8&#xff0c;其中R就是r…

【ARM 裸机】I.MX 启动方式之启动头文件 1

接上一节&#xff1a;【ARM 裸机】I.MX 启动方式之启动设备的选择&#xff1b; 2、启动头文件 当 BOOT_MODE1 为 1&#xff0c;BOOT_MODE0 为 0 的时候此内部 BOOT 模式&#xff0c;在此模式下&#xff0c;芯片会执 行内部的 BOOT ROM 代码&#xff0c;这段 BOOT ROM 代码会进…

常见的七种排序

目录 一、插入排序 1、直接插入排序 2、希尔排序&#xff08;缩小增量排序&#xff09; 二、选择排序 3、直接选择排序 4、堆排序 三、交换排序 5、冒泡排序 6、快速排序 四、归并排序 7、归并排序 五、总结 一、插入排序 1、直接插入排序 思路&#xff1a; i 用来…

Xamarin.Android中“ADB0020: Android ABI 不匹配。你正将应用支持的“armeabi-v7a;arm64-v8a”异常处理

这里写自定义目录标题 1、问题2、解决 1、问题 在Xamarin.Android中出现ADB0020: Android ABI 不匹配。你正将应用支持的“armeabi-v7a;arm64-v8a”ABI 部署到 ABI“x86_64;x86”的不兼容设备。应创建匹配其中一个应用 ABI 的仿真程序&#xff0c;或将“x86_64”添加到应用生成…

Parade Series - CoreAudio Loopback

Scenario 鉴于业务场景需要&#xff0c; 经过技术路径探索&#xff0c; 发现 comtypes 兼容性过于混乱&#xff0c;故而考虑整合一个 CoreAudio 的轮子dll来解决实际问题&#xff01;std::StringStream ⇒ std::ios::binary ⇒ std::ofstream Loopback.dll #ifndef _DLL_C…

【nvm最新解决方案】Node.js v16.20.2 is not yet released or available

【nvm最新解决方案】Node.js v16.20.2 is not yet released or available 解决办法&#xff1a;下载想安装的node压缩包&#xff0c;放入nvm对应目录。 2024年最新node压缩包地址&#xff1a;https://nodejs.org/dist/ 1、选择对应的node版本&#xff1a;例如&#xff0c;我选的…

如何创建响应式HTML电子邮件模板

在这个适合初学者的指南中&#xff0c;你将学习如何创建一个响应式电子邮件模板。你将跟随逐步说明以及代码片段设计一个在任何设备上都看起来很棒的电子邮件模板。 这个项目非常适合渴望掌握电子邮件设计基础的新手&#xff01; &#xff08;本文视频讲解&#xff1a;java56…

怎么用手机远程控制电脑 远程控制怎么用

怎么用手机远程控制电脑&#xff1a;远程控制怎么用 在这个科技日新月异的时代&#xff0c;远程控制电脑已经成为了很多人的需求。有时&#xff0c;我们可能在外出时突然需要访问家中的电脑&#xff0c;或者在工作中需要远程操控办公室的电脑。这时&#xff0c;如果能用手机远…

Spring 声明式事务控制

1. 编程式事务控制相关对象 1.1 PlatformTransactionManager PlatformTransactionManager 接口是 spring 的事务管理器&#xff0c;它提供了我们常用的操作事务的方法。 PlatformTransactionManager 是接口类型&#xff0c;不同的 Dao 层技术则有不同的实现类。例如:Dao层技…

【Spring】Spring源码中占位符解析器PropertyPlaceholderHelper的使用

开发中经常需要使用到占位符&#xff0c;情况较为复杂时总是手工替换处理显得比较的繁琐&#xff0c;加之往往手工所写效率比不上框架自带的现有方法来的更好更快。Spring在处理yml配置文件时&#xff0c;对于yml文件名的占位符替换处理便是使用了占位符解析器PropertyPlacehol…

深入了解PBKDF2:密码学中的关键推导函数

title: 深入了解PBKDF2&#xff1a;密码学中的关键推导函数 date: 2024/4/20 20:37:35 updated: 2024/4/20 20:37:35 tags: 密码学对称加密哈希函数KDFPBKDF2安全密钥派生 第一章&#xff1a;密码学基础 对称加密和哈希函数 对称加密&#xff1a;对称加密是一种加密技术&…

Windows COM技术:COM介绍、代码演示。

目录 步骤一&#xff1a;理解COM技术 介绍COM的基础知识 1. COM的目的和特点 2. COM的关键概念 3. COM的实现 4. COM与DCOM、ActiveX 讨论COM的用途 1. 软件自动化 2. 插件和扩展 3. 跨语言开发 4. 分布式计算 5. 系统级组件 6. 网络浏览器插件 步骤二&#xff1a…

开源贡献代码之​探索一下CPython

探索一下Cython 本篇文章将会围绕最近给Apache提的一个feature为背景&#xff0c;展开讲讲CPython遇到的问题&#xff0c;以及尝试自己从0写一个库出来&#xff0c;代码也已经放星球了&#xff0c;感兴趣的同学可以去下载学习。 0.背景 最近在给apache arrow提的一个feature因为…

【做一名健康的CSDNer】程序员如何早日脱单?

程序员脱单的策略可以从以下几个方面着手&#xff1a; 拓展社交圈&#xff1a;参加技术交流会、行业聚会、开源社区活动等&#xff0c;不仅可以提升技术能力&#xff0c;还可以结识更多志同道合的人&#xff0c;其中可能就包括潜在的伴侣65。 改善形象和性格&#xff1a;注意个…

【GIS教程】ArcGIS做日照分析(附练习数据下载)

我国对住宅日照标准的规定是:冬至日住宅底层日照不少于1小时或大寒日住宅层日照不少于2小时(通常以当地冬至日正午12时的太阳高度角作为依据)。因冬至日太阳高度角最低&#xff0c;照射范围最小&#xff0c;如果冬至日12&#xff1a;00建筑物底层能够接收到阳光&#xff0c;那么…

探索边缘计算:技术的新疆界

探索边缘计算&#xff1a;技术的新疆界 在当今迅速发展的数字化时代&#xff0c;云计算作为数据处理的主力军已广泛应用。但是&#xff0c;随着物联网&#xff08;IoT&#xff09;设备的急剧增加和数据生成速率的加快&#xff0c;云计算面临着种种挑战。边缘计算因此诞生&…

python爬虫-----深入了解 requests 库下篇(第二十五天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

[阅读笔记15][Orca]Progressive Learning from Complex Explanation Traces of GPT-4

接下来是微软的Orca这篇论文&#xff0c;23年6月挂到了arxiv上。 目前利用大模型输出来训练小模型的研究都是在模仿&#xff0c;它们倾向于学习大模型的风格而不是它们的推理过程&#xff0c;这导致这些小模型的质量不高。Orca是一个有13B参数的小模型&#xff0c;它可以学习到…

从零自制docker-11-【pivotRoot切换实现文件系统隔离】

文章目录 busyboxdocker run -d busybox topcontainerId(docker ps --filter "ancestorbusybox:latest"|grep -v IMAGE|awk {print $1})docker export -o busybox.tar $containerId or sudo docker export 09bbf421d93f > ./busybox.tar tar -xvf busybox.tar -C …