【面向对象语言三大特性之 “继承”】(一)

简介: 【面向对象语言三大特性之 “继承”】(一)

1.继承的概念及定义

1.1继承的概念

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

我们来举个栗子:

首先我们定义一个基类Person:

class Person
{
public:
  void Print()
  {
    cout << "name:" << _name << endl;
    cout << "age:" << _age << endl;
  }
protected:
  string _name = "peter";//  姓名
  int _age = 18;//  年龄
};

再定义两个子类Stuend和Teacher,这两个子类是继承了父类的成员(成员函数+成员变量)

我们可以通过监视窗口来查看:


fbfafe2f43ed42d29c30ae787616aa2c.png

不难发现的确子类继承了父类的成员。

1.2 继承定义

1.2.1定义格式

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

7dda46fa428e4689bddff9d5e16b0af4.png

1.2.2继承关系和访问限定符

de5cb971420b4d93ba465c1d2390b69a.png

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

image.png

总结:

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  来进行识别后进行安全转换。(ps:这个我们后面再讲解,这里先了解一下)

看下面这样一段代码:

int main()
{
  Student sobj;
  Person pobj = sobj;
  Person* pp = &sobj;
  Person& rp = sobj;
  return 0;
}

我们发现编译既然是没有错误的,这里就将子类切片给了父类。

(注意:切片行为是不存在隐式类型转换的,是直接将子类切片给父类)


aff767ab8fb34e209743792b1d574be7.png

但是我们要注意:基类对象不能赋值给派生类对象 基类的指针可以通过强制类型转换赋值给派生类的指针 (这样会有越界的风险)

    //基类对象不能赋值给派生类对象
    sobj = pobj;
    //基类对象不能引用给派生类对象
    Person p;
  Student& rs = p;
    //基类的指针可以通过强制类型转换赋值给派生类的指针
    pp = &sobj
    Student* ps1 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
    ps1->_No = 10;

3.继承中的作用域

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

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

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

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

就像下面这段程序:

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

这段程序能够正确运行:

ae8c675ff81b45769e74e2afe2746ea8.png

通过以前学习的知识我们也知道当同名变量出现在一起时会优先使用局部,而这里_num出现在两个不同的类域中,默认情况就在本域去寻找,指定类域后就在指定类域去寻找。这种情况就叫做隐藏。

再来看看一段程序:

// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
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);
};

由于fun()函数在不同作用域中,所以他们构成隐藏而不是重载。


目录
相关文章
|
11月前
|
存储 API 数据安全/隐私保护
如何使用Web Storage进行状态持久化?
如何使用Web Storage进行状态持久化?
|
10月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
Android开发 Swift iOS开发
iOS和安卓作为主流操作系统,开发者需了解两者差异以提高效率并确保优质用户体验。
【10月更文挑战第1天】随着移动互联网的发展,智能手机成为生活必需品,iOS和安卓作为主流操作系统,各有庞大的用户群。开发者需了解两者差异以提高效率并确保优质用户体验。iOS使用Swift或Objective-C开发,强调简洁直观的设计;安卓则采用Java或Kotlin,注重层次与动画。Swift和Kotlin均有现代编程特性。此外,iOS设备更易优化,而安卓需考虑更多兼容性问题。iOS应用仅能通过App Store发布,审核严格;安卓除Google Play外还可通过第三方市场发布,审核较宽松。开发者应根据需求选择合适平台,提供最佳应用体验。
500 3
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
811 1
|
11月前
|
人工智能 供应链 搜索推荐
AI+电商API:智能推荐、动态定价与自动化运营的未来
在电商竞争日益激烈的今天,AI与电商API的深度融合正重塑行业格局。通过智能推荐、动态定价与自动化运营,AI+电商API助力企业精准洞察用户需求、优化价格策略、提升运营效率,推动个性化、高效能的智慧电商发展,为企业打开未来增长新空间。
|
监控 安全 测试技术
避免15个常见的API测试错误
API测试是现代软件开发中的关键环节,但测试人员常陷入15个常见错误,如忽略文档、遗漏错误响应、缺乏自动化等。这些问题可能导致API的可靠性、安全性及性能下降。本文详细解析这些错误并提供解决建议。Apipost作为全方位工具,支持自动化测试、动态数据处理与安全性验证,助力高效测试,确保API在各种场景下稳定运行。通过避免这些陷阱,团队可显著提升API质量。
|
供应链 搜索推荐 数据可视化
《电商管理:精准营销与客户忠诚度培育》
本文概述了电商管理的重要性和关键领域,包括供应链管理、客户关系管理、数据管理和营销推广管理。文章指出,有效的电商管理能帮助企业应对激烈的市场竞争、多变的消费者需求和技术快速更新等挑战,实现高效运营和可持续发展。特别介绍了“板栗看板”这一创新工具,强调其在提升电商管理效率方面的显著作用。
《电商管理:精准营销与客户忠诚度培育》
|
人工智能 监控 安全
设计:智能医疗设备管理系统——AI医疗守护者
该系统将结合人工智能技术与区块链技术,实现对医疗设备的智能化管理。目标是提高医疗设备的管理效率,确保医疗设备的数据安全,优化医疗资源的配置,提升医疗服务质量。
|
存储 数据管理 关系型数据库
数据库分库分表的原因?
分库分表通过减少单库单表负担来提升查询性能。垂直切分按业务耦合度将表或列分布于不同库或表中,减少数据量,优化性能。水平切分则按数据逻辑关系将表分散至多库多表,减小单表数据量,实现分布式处理。选择方式需根据具体需求决定。
473 19
|
编译器 C语言
【C语言】常量的 “前缀和后缀” 大通关!
在C语言中,常量的前缀和后缀用于明确指定常量的类型和进制系统。前缀主要用于区分不同进制的数字常量,而后缀则用于区分不同类型的整数和浮点数。正确使用前缀和后缀,可以提高代码的可读性和可维护性,确保编译器正确地理解和处理常量。
1048 1