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

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

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

相关文章

Geoserver的RESTful接口使用

概述 GeoServer提供了一个RESTful接口&#xff0c;客户端可以通过该接口获取有关实例的信息并进行配置更改。REST接口使用简单的HTTP调用&#xff0c;通过客户端就可以配置GeoServer&#xff0c;而无需使用Web管理接口。 Geoserver中的关系 工作区、数据源、图层、图层组以及…

漂亮的个人主页源码

源码介绍 漂亮的个人主页源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果截图 源码下载 漂亮的个人主页源码

基于 RT-Thread 的 PPP Device 软件包的详细使用以及AT通用配网过程

一、AT通用上网过程 网络初始化流程 一般情况如下 1、先上电复位模块&#xff1b; 2、间隔一直发送 AT\r 等待模组响应,表示模组启动&#xff0c;并且调试好了波特率&#xff1b; 3、发送ATCPIN?\r 测试卡是否插好&#xff1b; 4、发送 ATCSQ\r 查询信号质量&#xff0c;只有…

vue element ui 打开弹窗出现黑框问题

文章目录 问题描述解决方案 问题描述 大家好&#xff01;今天是2024年4月20日 | 农历三月十二&#xff0c;周六的我又做在公司里面写起了代码 今天在做项目的时候遇到一个奇怪的问题&#xff0c;如下图所示&#xff1a; 因为这个页面我做了两个弹框&#xff0c;先弹出来第一个弹…

Linux 服务器硬件及RAID配置实战

服务器详解 服务器分类 可以分为&#xff1a;塔式服务器、机架服务器、刀片服务器、机柜服务器等。 其中以机架式居多 服务器架构 服务器品牌&#xff1a; 戴尔、AMD、英特尔、惠普、华为、华3&#xff08;H3C&#xff09;、联想、浪潮、长城 服务器规格&#xff1a; 规格…

Unity射击游戏开发教程:(2)实例化和销毁游戏对象

现在我们有了“飞船”,我们可以在屏幕上移动它,现在我们需要发射一些激光!与宇宙飞船一样,我们将让事情变得简单并使用 Unity 自己的基本形状。舱体的效果很好,所以我们来创建一个。 我们保存了有关位置、旋转和缩放的信息。我们想要缩小这个对象,假设每个轴上缩小到 0.2…

oracle操作系统OS认证和密码文件认证

1 说明 1.1 常见认证方式 Oracle登录认证方式主要涉及到如何验证用户身份以访问数据库。Oracle数据库提供了多种认证机制来确保数据的安全性和访问控制&#xff0c;每种方式都有其特定的使用场景和安全性考虑。以下是Oracle中常见的登录认证方式&#xff1a; 1、基于操作系统…

【Redis】string数据类型

文章目录 常用命令setsetnx & NXXXsetex & EXpsetex & PX msetget & mgetincr & decrincrby & decrbyincrbyfloatappendgetrangesetrangestrlen 内部编码 字符串类型是 Redis 最基础的数据类型。 在redis中所有的键都是 string 类型&#xff0c;其他的…

机器学习基本流程

Jupyter Notebook 代码连接&#xff1a; machine_learning_demo machine_learning_ensembles Step 1: Imports and Configuration import pandas as pd import numpy as np import copy import json import pickle import joblib import lightgbm as lgb import optuna impor…

【机器学习】农田智能监控系统的实践探索

机器学习赋能现代农业&#xff1a;农田智能监控系统的实践探索 一、机器学习在现代农业中的重要作用二、机器学习在农田智能监控系统中的应用三、农田智能监控系统的实践意义 在科技飞速发展的今天&#xff0c;机器学习技术正以其强大的数据处理和模式识别能力&#xff0c;逐步…

单点登录实现:一次登录,到处运行

单点登录&#xff1a;一次登录&#xff0c;到处运行 举个场景&#xff0c;假设我们的系统被切割为N个部分&#xff1a;商城、论坛、直播、社交…… 如果用户每访问一个模块都要登录一次&#xff0c;那么用户将会疯掉&#xff0c; 为了优化用户体验&#xff0c;我们急需一套机制…

【小程序】IOS wx小程序解压获取源文件

根据自己手机的系统&#xff0c;获取wx小程序的缓存目录 一、微信小程序文件存放路径 安卓&#xff1a; /data/data/com.tencent.mm/MicroMsg/{{user哈希值}}/appbrand/pkg/iOS越狱&#xff1a; /User/Containers/Data/Application/{{系统UUID}}/Library/WechatPrivate/{{user…