C++第二十九弹---C++继承机制深度剖析(上)

news2024/12/24 2:57:50

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1.继承的概念及定义

1.1继承的概念

1.2 继承定义

1.2.1定义格式

1.2.2继承关系和访问限定符

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

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

3.继承中的作用域


1.继承的概念及定义


1.1继承的概念

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

下面是父类与子类的基本概念:

  • 父类(基类):父类是一个更一般、更抽象的类,它定义了一组属性和方法,这些属性和方法会被其子类所继承。父类通常用于表示一种广泛的、更通用的概念或对象。
  • 子类(派生类):子类是一个更具体、更特殊的类,它继承了父类的属性和方法,并可以添加新的属性或 覆盖(重写) 父类的方法。子类通常用于表示一种更具体、更特殊的概念或对象,这些对象属于父类所定义的广泛类别中的一部分。

下面我们可以通过学校的成员举例,学校有学生,老师,宿管,保安等成员,这些成员都有相同的特性,有自己的名字,年龄,家庭地址,但是因为身份不同也会有不同的属性,例如学生有学号,宿舍号,老师有工号,把办公室等等,这种有相同属性又有不同属性的情况则可以通过继承来进行描述,如下图:

代码举例: 

// 父类/基类
class Person
{
public:
	void Print()
	{
		cout << "_name = " << _name << endl;
		cout << "_age = " << _age << endl;
	}
protected:
	string _name = "jack";
private:
	int _age = 18;
};
// 子类/派生类,继承Person
class Student :public Person
{
protected:
	int _stuid;
};
// 子类/派生类,继承Person
class Teacher : public Person
{
protected:
	int _jobid;
};

继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
Student和Teacher复用了Person的成员

下面我们使用监视窗口查看Student和Teacher对象,可以看到变量的复用。

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

1.2 继承定义


1.2.1定义格式


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

1.2.2继承关系和访问限定符


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

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

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

父类/基类

class Person
{
public:
    void print()
    {
        cout<<_age<<endl;
    }
// 成员访问限定符为私有
private:
	int _age = 18;
};

子类 /派生类

//class Student : private Person      //继承方式为私有
//class Student : protected Person    //继承方式为保护
class Student : public Person         //继承方式为公有
{
protected:
	int _stuid;
};

 主函数

int main()
{
	Student s;
	//cout << s._age << endl;// 基类的私有成员无法访问
	s.print();// 虽然不能直接访问父类的private成员,但是可以父类的print函数间接访问
    return 0;
}

监视窗口查看成员

测试结果 

 理解:

基类的私有成员不能直接进行访问,但是可以通过父类的公有成员函数间接进行访问。


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

父类/基类

class Person
{
protected:
	string _name = "jack";
private:
	int _age = 18;
};

子类/派生类

class Student : public Person
{
public:
	void print()
	{
		cout << _name << endl;
		//cout << _age << endl;// 屏蔽私有成员则可以访问
	}
protected:
	int _stuid;
};

主函数

int main()
{
	Student s;
	s.print();// 派生类中可以访问父类protected成员
	return 0;
}

 


3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他
成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected
> private。


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

父类/基类

class Person
{
protected:
	string _name = "jack";
private:
	int _age = 18;
};

子类/派生类

// struct的默认访问限定符和默认继承方式均为public
// class 的默认访问限定度和默认继承方式均为private

//class Student : Person  
struct Student : Person 
{
	void print()
	{
		cout << _name << endl;
	}
protected:
	int _stuid;
};

主函数 

int main()
{
	Student s;
	s.print();
	return 0;
}


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

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


派生类对象 可以赋值基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。(公有继承才可以,且不产生临时对象)

class Person
{
public:
	string _name = "jack";
private:
	int _age = 18;
};
class Student : public Person
{
protected:
	int _stuid;
};

切片/切割 

int main()
{
	//切割/切片赋值兼容   子类赋值给父类
	Student s;
	Person p = s;//1.子类对象赋值给父类对象

	Person& ref = s;//2.子类对象赋值给父类的引用
	Person* ptr = &s;//3.子类对象赋值给父类的指针

	ref._name += 'x';//ref的修改会使子类s修改,证明不会产生临时对象
	ptr->_name += 'y';
	return 0;
}


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

class Person
{
public:
	string _name = "jack";
private:
	int _age = 18;
};
class Student : public Person
{
protected:
	int _stuid;
};

int main()
{
	Student s;
	Person p;

	s = p;//基类对象不能赋值给派生类对象。
	return 0;
}


基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。
 

3.继承中的作用域


1. 在继承体系中基类和派生类都有独立的作用域(可以创建同名成员)。

class Person
{
public:

protected:
	string _name;
	int _age;
	int _num = 0;
};

class Student : public Person
{
public:

private:
	int _stuid;
	int _num = 1;
};
int main()
{
	Student s;//默认访问子类/派生类

	return 0;
}

2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏
也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问

class Person
{
public:
	void fun()
	{
		cout << "_name = " << _name << endl;
		cout << "_age = " << _age << endl;
	}
protected:
	string _name;
	int _age;
	int _num = 0;
};

class Student : public Person
{
public:
	void fun()
	{
		//Person的_num 与 Student的_num构成隐藏关系
		//父类子类成员变量均有_num
		cout << _num << endl;
		//访问不报错,默认访问子类  局部域 全局域 命名空间域 类域(基类 派生类)
		cout << Person::_num << endl;//指定基类域   ::显示访问
	}
private:
	int _stuid;
	int _num = 1;
};

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

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

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

class B : public A
{
public:
	void func(int i)
	{
		func();
		//A::func();//指定域调用则可以
        cout << "func(int i)->" << i << endl;
	}
};

func()函数与父类func()函数构成隐藏,根据函数调用的就近原则,会先调用子类的func函数,但是发现参数不匹配,因此会编译报错。解决办法是通过指定作用域调用。

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


 

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

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

相关文章

常见的手电筒芯片功能模式选型 单路双路可用

常见的手电筒芯片如下 单双路输出 带充电功能和不带充电功能的 外围结构简单、无需多余的元器件 搜恒森宇电子了解更多相关功能&#xff01; 首页

Spring随笔

Spring随笔 BeanFactory和ApplictionContextbean增强 AutowiredAnnotationBeanPostProcessor工厂增强 BeanFactory和ApplictionContext BeanFactory装载了bean实例&#xff0c;一个容器&#xff0c;提供了对bean的增删改查 ApplictionContext继承了factory&#xff0c;除此之外…

MyBatis基础配置

一、M y B a t i s 配 置 文 件 1.为什么学习MyBatis配置文件 功能&#xff1a;构建SqlSessionFactory的依据。 意义&#xff1a;MyBatis最为核心的内容&#xff0c;对MyBatis的使用影响很大。 注意&#xff1a;配置文件的层次顺序不能颠倒&#xff0c;一旦颠倒会出现异常。 …

NLP与搜广推常见面试问题

1 auc指标 AUC的两种意义 一个是ROC曲线的面积另外一个是统计意义。从统计学角度理解&#xff0c;AUC等于随机挑选一个正样本和负样本时&#xff0c;模型对正样本的预测分数大于负样本的预测分数的概率。下图为搜广推场景下的一个计算auc的例子

如何查找OBS的终端节点(Endpoint)和访问域名

目录 一、参考链接二、终端节点&#xff08;Endpoint&#xff09;三、访问域名 一、参考链接 https://support.huaweicloud.com/productdesc-obs/obs_03_0152.html 二、终端节点&#xff08;Endpoint&#xff09; OBS为每个区域提供一个终端节点&#xff0c;终端节点可以理解…

JVM性能调优全指南:高流量电商系统的最佳实践

1.G1(Garbage-First) 官网: G1 Garbage Collection G1收集器是Java 7中引入的垃圾收集器,用于替代CMS(Concurrent Mark-Sweep)收集器。它主要针对大内存、多核CPU环境下的应用场景,具有以下特点: 分代收集:G1仍然保留了分代的概念,但新生代和老年代不再是物理隔离的,…

37 列表推导式

列表推导式&#xff08;list comprehension) 也成为列表解析式&#xff0c;可以使用非常简洁的方式对列表或其他可迭代对象的元素进行遍历、过滤或再次计算&#xff0c;快速生成满足特定需求的新列表&#xff0c;代码非常简洁&#xff0c;具有很强的可读性&#xff0c;是 pytho…

抖音视频素材网站有哪些?非常好用的5个抖音视频素材库分享

在打造引人入胜的抖音视频时&#xff0c;选择高品质的视频素材至关重要。优选的素材不仅能够显著提升视频的吸引力&#xff0c;还能让你的作品在众多视频中突出重围。对于抖音创作者而言&#xff0c;让我们探索一些备受推崇的视频素材平台&#xff0c;帮助你制作出既专业又引人…

C:操作符介绍-学习笔记

目录 引言&#xff1a; 1、操作符的分类&#xff1a; 2、原码&#xff0c;反码&#xff0c;补码 2.1 介绍 2.2 作用 3、移位操作符&#xff1a;>>、 << 3.1 左移操作符 &#xff1a;<< 3.1.1 正整数移动 3.1.2 负整数移动 3.2 右移操作符&#xff…

Unity GameObject学习笔记

GameObject成员变量 GameObject静态方法 //准备用来克隆的对象//1.直接是场景上的某个对象//2.可以是一个预制体对象public GameObject Myobj; #region 知识点二 GameObject中的静态方法创建自带几何体只要得到了一个GameObject对象 我就可以得到它身上挂载的任何脚本信息GameO…

YotoR(You Only Transform One Representation)

本文介绍了一种名为YotoR&#xff08;You Only Transform One Representation&#xff09;的新型深度学习目标检测模型。该模型将Swin Transformers与YoloR架构相结合。在自然语言处理领域引起革命的Transformer技术&#xff0c;如今同样对计算机视觉产生了深远影响&#xff0c…

7.29 模拟赛总结 平面图欧拉定理

复盘 7:40 开题 开题失败&#xff0c;由于前一天有 cf&#xff0c;模拟赛移到下午了 13:45 开题 看 T1&#xff0c;题意很抽象&#xff0c;理清后发现&#xff1a;这直接 dj 不就行了&#xff1f;不会错吧不会错吧&#xff0c;看着 n 1000 n1000 n1000 的数据范围还是不确…

java实现权重轮询算法

package com.example.demo.demos.web.nginx;import java.util.ArrayList; import java.util.List;public class WeightedRoundRobin {private static List<Server> servers new ArrayList<>(); // 存储服务器的列表private static int currentIndex -1; // 当前服…

APP测试基本流程以及APP测试要点梳理,成功入职就靠它了

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

【MATLAB源码-第161期】基于matlab的OQPSK系统仿真,输出误码率曲线图,眼图以及各节点信号图像对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 正交四相移相键控&#xff08;OQPSK&#xff0c;Orthogonal Quadrature Phase Shift Keying&#xff09;是一种数字调制技术&#xff0c;它在传统的QPSK&#xff08;Quadrature Phase Shift Keying&#xff0c;四相移相键控&…

Linux--序列化与反序列化

序列化 序列化是指将数据结构或对象状态转换成可以存储或传输的格式的过程。在序列化过程中&#xff0c;对象的状态信息被转换为可以保持或传输的格式&#xff08;如二进制、XML、JSON等&#xff09;。序列化后的数据可以被写入到文件、数据库、内存缓冲区中&#xff0c;或者通…

jupyter notebook报错: No module named ‘pandas‘

打开jupyter notebook&#xff0c;新建Python3&#xff0c;编写代码import pandas as pd jupyter notebook: No module named pandas 解决办法 :打开Anaconda prompt&#xff0c;输入pip install pandas,安装时可能因为网速原因失败&#xff0c;用同样的命令再试一次&#xf…

数据透视表(二)

文章目录 导入外部数据源创建数据透视表Query 工具下的数据透视表创建如何统计业绩成交情况创建组利用函数构建辅助列创建组手动创建多样分组创建组区间统计创建组按年月日统计数据透视表的多种统计方法计算字段 导入外部数据源创建数据透视表 点击数据选项卡下数据-获取外部数…

04.FreeRTOS任务创建

04. FreeRTOS任务创建与任务删除 1. FreeRTOS创建和删除任务相关API函数 函数描述xTaskCreate()动态方式创建任务xTaskCreateStatic()静态方式创建任务xTaskCreateRestricted()动态方式创建使用 MPU 限制的任务xTaskCreateRestrictedStatic()静态方式创建使用 MPU 限制的任务…

js_拳皇(下)

文章目录 架构设计视频演示碰撞检测碰撞检测函数 构想血条和计时器全屏后续工作 架构设计 一图胜千言 #mermaid-svg-erOUDyAO5t0XgYyU {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-erOUDyAO5t0XgYyU .error-icon{…