【C++】继承的概念和简单介绍、基类和派生类对象复制转换、继承中的作用域、派生类的默认成员函数

文章目录

  • 继承
    • 1.继承的概念和简单介绍
      • 1.1继承的概念
      • 1.2继承的定义
    • 2.基类和派生类对象复制转换
    • 3.继承中的作用域
    • 4.派生类的默认成员函数
    • 5.继承与友元
    • 6.继承与静态成员

继承

1.继承的概念和简单介绍

1.1继承的概念

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

  在C++中,继承是一种面向对象编程的概念,它允许一个类(称为派生类或子类)从另一个类(称为基类或父类)继承属性和方法。继承使得派生类可以重用基类的代码,并且可以在此基础上添加新的功能或修改现有的功能。

1.2继承的定义

在这里插入图片描述

  (1)公有继承(public inheritance):基类中的公有成员在派生类中仍然是公有的,保持了访问权限不变。派生类可以直接访问基类的公有成员,但不能直接访问基类的私有成员。

class 基类 {
public:// 公有成员
};class 派生类 : public 基类 {
public:// 派生类的成员
};


  (2)保护继承(protected inheritance):基类中的公有成员在派生类中变为保护的,保护成员只能在派生类内部或其派生类中访问,外部无法访问。

class 基类 {
protected:// 保护成员
};class 派生类 : protected 基类 {
public:// 派生类的成员
};


  (3)私有继承(private inheritance):基类中的公有成员在派生类中变为私有的,私有成员只能在派生类内部访问,外部无法访问。

class 基类 {
private:// 私有成员
};class 派生类 : private 基类 {
public:// 派生类的成员
};

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

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


总结:

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

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

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

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

2.基类和派生类对象复制转换

  在C++中,基类和派生类之间的对象复制转换是指将一个基类对象赋值给一个派生类对象或将一个派生类对象赋值给一个基类对象的操作。 这种操作可以通过以下两种方式进行:

  (1)赋值兼容(切片)(slicing):将派生类对象赋值给基类对象时,会发生对象切片。只会复制基类部分的成员,派生类特有的成员将被丢弃。

  (2)引用/指针转换: 可以使用基类的引用或指针来引用或指向派生类对象。这样做可以实现基类和派生类对象之间的相互转换,但是只能访问到基类部分的成员,无法访问到派生类特有的成员。

在这里插入图片描述

class Person
{
protected :string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public :int _stuid ; // 学号
};void Test ()
{Student sobj ;// 1.子类对象可以赋值给父类对象/指针/引用Person pobj = sobj ;Person* pp = &sobj;Person& rp = sobj;//2.基类对象不能赋值给派生类对象sobj = pobj;// 3.基类的指针可以通过强制类型转换赋值给派生类的指针pp = &sobjStudent* ps1 = (Student*)pp; // 这种情况转换时可以的。ps1->_No = 10;pp = &pobj;Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题ps2->_No = 10;
}

总结:

  (1)派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。

  (2)基类对象不能赋值给派生类对象。

  (3)基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。

3.继承中的作用域

  (1)在继承体系中基类和派生类都有独立的作用域。

  (2)子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏也叫重定义。

  (3)需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

  (4)注意在实际中在继承体系里面最好不要定义同名的成员。

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);
};
//此时A和B中的fun构成隐藏,不是重载,因为fun不在同一个作用域
//B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏
//重载需要函数在同一个作用域中,函数名相同,参数列表不同才可以实现


重载和隐藏的区别

  (1)重载(overloading):重载是指在同一个作用域内,可以定义多个具有相同名称但参数列表不同的函数。 重载函数可以根据参数的不同类型或数量来决定调用哪个函数。重载函数的特征是具有相同的函数名,但参数列表不同。

void 函数名(int 参数1);
void 函数名(double 参数1);
void 函数名(int 参数1, int 参数2);
//此时函数实现重载


  (2)隐藏(hiding):隐藏是指在派生类中定义了与基类中同名的成员(变量或函数),从而隐藏了基类的同名成员。 当在派生类中使用同名成员时,会优先访问派生类中的成员,而不是基类中的成员。如果需要访问基类的同名成员,可以使用作用域解析运算符(::)来指定基类的作用域。

class 基类 {
public:int 成员变量;void 成员函数();
};class 派生类 : public 基类 {
public:int 成员变量; // 隐藏了基类的成员变量void 成员函数(); // 隐藏了基类的成员函数
};派生类 obj;
obj.成员变量; // 访问派生类的成员变量
obj.基类::成员变量; // 访问基类的成员变量
obj.成员函数(); // 调用派生类的成员函数
obj.基类::成员函数(); // 调用基类的成员函数
//此时的成员函数构成隐藏

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

  派生类的默认成员函数是指在派生类中没有显式定义的成员函数,而是通过继承基类的成员函数来自动生成的函数。

  和普通的类一样派生类的默认成员函数包括以下几种:

  (1)默认构造函数(Default Constructor): 如果派生类没有显式定义构造函数,那么编译器会自动生成一个默认构造函数。默认构造函数会调用基类的默认构造函数来初始化基类的成员,然后再初始化派生类的成员。

  (2)拷贝构造函数(Copy Constructor): 如果派生类没有显式定义拷贝构造函数,那么编译器会自动生成一个拷贝构造函数。拷贝构造函数会调用基类的拷贝构造函数来初始化基类的成员,然后再初始化派生类的成员。

  (3)赋值运算符(Assignment Operator): 如果派生类没有显式定义赋值运算符,那么编译器会自动生成一个赋值运算符。赋值运算符会调用基类的赋值运算符来赋值基类的成员,然后再赋值派生类的成员。

派生类的默认成员函数如何生成

  1.构造函数

(1)派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。 如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

(2)派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

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

(4)派生类对象初始化先调用基类构造再调派生类构造。

  2.析构函数

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

(2)派生类对象析构清理,先调用派生类析构,再调基类的析构。

在这里插入图片描述

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;
}//人数: 4
//人数: 0

当然继承还有很多的知识点,这里只是对C++继承的部分介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉

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

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

相关文章

Spring Boot 系列4 -- 统一功能处理

目录 前言 1. Spring AOP 用户统⼀登录验证的问题 1.1 自定义拦截器 1.2 配置拦截器并配置拦截的规则 1.3 拦截器的原理源码分析 2. 统一异常处理 2.1 实现统一异常处理 2.2 测试统一异常处理 3. 统一的数据格式返回 3.1 统⼀数据返回格式的实现 3.2 测试统一的数据返…

QT 使用单例模式

目录 1. 单例模式介绍 2.单例模式实现 1. 单例模式介绍 有些时候我们在做 qt 项目的时候,要用到很多类. 例如我们用到的类有 A,B,C,D. 其中,A 是 B,C,D 中都需要用到的类,A 类非常的抢手. 但是,A 类非常的占内存,定义一个 A 对象需要 500M 内存,假如在 B,C,D 中都定义一个 A 类…

浏览器不同源的页面之间如何跨域通信

目录 1&#xff0c;需求2&#xff0c;难点3&#xff0c;思路浏览器不同源的页面之间如何跨域通信&#xff1f; 4&#xff0c;实现第1版第2版最终版其他的问题1&#xff0c;页面路径需完全一致。2&#xff0c;事件注册问题 1&#xff0c;需求 现在有2个项目&#xff0c;页面路径…

Xilinx A7开发板LVDS IO无输出问题解决方法

使用A7-35T FGG484的FPGA开发板bank16上的IO作为差分LVDS的输入输出&#xff0c;搭建输入输出测试工程发现LVDS可以输入、无法输出。查阅UG471&#xff0c;找到如下信息&#xff1a; 手册中已经针对A7的LVDS做了明确的应用说明&#xff1a; &#xff08;1&#xff09;HP bank上…

数组的学习

数组学习 文章目录 数组来由数组的使用数组的内存图变量声明和args参数说明声明分配空间值的省略写法数组的length属性数列输出求和判断购物金额结算Arrays的sort和toString方法Arrays的equals和fill和copyOf和binarySearch方法字符数组顺序和逆序输出 数组来由 录入30个学生…

telnet检验网络能不能通

telnet检测网络能不能通&#xff08;ip地址端口号&#xff09;

阿里云平台WoSignSSL证书应用案例

沃通CA与阿里云达成合作并在阿里云平台上线WoSign品牌SSL证书。自上线以来&#xff0c;WoSignSSL证书成为阿里云“数字证书管理服务”热销证书产品&#xff0c;获得阿里云平台客户认可&#xff0c;助力阿里云平台政府、金融、教育、供应链、游戏等各类行业客户实现网站系统数据…

利用OpenCV实现图像拼接

一、介绍 图像拼接. 二、分步实现 要实现图像拼接&#xff0c;简单来说有以下几步&#xff1a; 对每幅图进行特征点提取对对特征点进行匹配进行图像配准把图像拷贝到另一幅图像的特定位置对重叠边界进行特殊处理 PS&#xff1a;需要使用低版本的opencv&#xff0c;否则无法使…

扩散模型实战(二):扩散模型的发展

推荐阅读列表&#xff1a; 扩散模型实战&#xff08;一&#xff09;&#xff1a;基本原理介绍 扩散模型从最初的简单图像生成模型&#xff0c;逐步发展到替代原有的图像生成模型&#xff0c;直到如今开启 AI 作画的时代&#xff0c;发展速度可谓惊人。下面介绍一下2D图像生成相…

Unity Image(RawImage) 实现按轴心放大缩小,序列化存储轴心信息,实现编译器窗口保存轴心

工作时分配给我的要实现的功能&#xff0c;写的时候遇到挺多的坑的&#xff0c;在此记录一下 效果 放大缩小的效果 2.编译器扩展窗口记录 实现点 1.Json序列化存储图片轴心位置, 放大倍率&#xff0c;放大所需要的事件 2.用了编译器扩展工具便于保存轴心信息坑点 1.Imag…

FANUC机器人SRVO-300机械手断裂故障报警原因分析及处理办法

FANUC机器人SRVO-300机械手断裂故障报警原因分析及处理办法 首先,我们查看报警说明书上的介绍: 总结:即在机械手断裂设置为无效时,机器人检测出了机械手断裂信号(不该有的信号,现在检测到了,所以报警) 使机械手断裂设定为无效/有效的具体方法:  按下示教器的MENU菜单…

弘扬“两弹一星”精神,勇攀科学技术高峰——道本科技商业大学党日活动圆满落幕

2023年8月2日&#xff0c;道本科技与商业大学携手举办了一场主题为“弘扬‘两弹一星’精神&#xff0c;勇攀科学技术高峰”的党日活动。本次活动旨在了解党领导下的中国核工业发展历程&#xff0c;传承和弘扬“两弹一星”精神&#xff0c;同时展示道本科技创新产品&#xff0c;…