新闻详情

新闻详情

首页 / 资讯中心 / 详情

【cpp】继承

发布时间:2026/9/12 21:07:20来源:尧图网络
【cpp】继承
1.继承的概念和定义1.1继承的概念继承inheritance机制是面向对象程序设计使代码可以复用的最重要的手段它允许我们在保持原有类特性的基础上进行扩展增加方法成员函数和属性成员变量这样产生新的类称派生类。继承呈现了面向对象程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用继承是类设计层次的复用。简单的说就好比之前c语言把常用的一个函数功能单独定义成一个函数而继承则是把不同类中相同的属性和方法定义到一个基类父类中比如下面中把老师类和学生类相同的属性和方法都放到Person基类中就可以复用这些成员不需要重复定义省去了很多麻烦。class Person { public: // 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity() { cout void identity() _name endl; } protected: string _name 张三; // 姓名 string _address; // 地址 string _tel; // 电话 int _age 18; // 年龄 }; class Student : public Person { public: // 学习 void study() { // ... } protected: int _stuid; // 学号 }; class Teacher : public Person { public: // 授课 void teaching() { //... } protected: string title; // 职称 };1.2继承的定义上面我们看到Person是基类父类StudentTeacher是派生类子类则定义格式如下其中继承方式有三种public、protected、private。访问方式也有三种public、protected、private。由于继承方式和访问方式的不同就会影响在派生类中对基类成员的访问一个有下面九种对应关系类成员 / 继承方式public 继承protected 继承private 继承基类的 public 成员派生类的 public 成员派生类的 protected 成员派生类的 private 成员基类的 protected 成员派生类的 protected 成员派生类的 protected 成员派生类的 private 成员基类的 private 成员在派生类中不可见在派生类中不可见在派生类中不可见基类private成员无论哪种继承派生类都不能直接访问public 继承访问权限保持不变protected 继承基类 public → protectedprivate 继承基类 public/protected 全部变成 private1.3继承类模板当基类是一个类模板的时候在派生类中使用基类的方法要指定类域否则会报错这和模板按需实例化有关具体看下面代码栈的实现继承库里面的vector实现//类模板继承 namespace gxy { templateclass T class stack:public vectorT { public: void push(const T x) { //push_back(x); error C3861: “push_back”: 找不到标识符 // 基类是类模板时需要指定一下类域 // 否则编译报错:error C3861: “push_back”: 找不到标识符 // 因为stackint实例化时也实例化vectorint了 // 但是模版是按需实例化push_back等成员函数未实例化所以找不到 vectorT::push_back(x); } void pop() { vectorT::pop_back(); } const T top() { return vectorT::back(); } bool empty() { return vectorT::empty(); } }; }2.基类和派生类之间的转换通常情况下我们把一个类型的对象赋值给另一个类型的指针或者引用时存在类型转换中间会产生临时对象所以需要加const如int alconst double da;public继承中就是一个特殊处理的例外派生类对象可以赋值给基类的指针/基类的引用而不需要加const这里的指针和引用绑定是派生类对象中的基类部分如下图所示。也就意味着一个基类的指针或者引用可能指向基类对象也可能指向派生类对象。派生类对象赋值给基类对象是通过基类的拷贝构造函数或者赋值重载函数完成的这两个函数的细节后面小节会细讲这个过程就像派生类自己定义部分成员切掉了一样所以也被叫做切割或者切片如下图中所示。但是基类对象不能赋值给派生类对象3.继承中的作用域隐藏规则1.在继承体系中基类和派生类都有独立的作用域。不能简单理解成是直接把基类添加到派生类2.派生类和基类中有同名成员派生类成员将屏蔽基类对同名成员的直接访问这种情况叫隐藏。在派生类成员函数中可以使用基类基类成员显示访问3.需要注意的是如果是成员函数的隐藏只需要函数名相同就构成隐藏。注意不是函数重载因为函数重载必须在同一个做用域4.注意在实际中在继承体系里面最好不要定义同名的成员。4.派生类的默认成员函数4.1 4个常见的默认成员函数默认成员函数我们写编译器会默认生成一个那么在派生类中他们是如何实现的此时我们应该理解继承的作用域基类与派生类各自拥有独立的作用域不能简单地认为基类被复制进了派生类。派生类可以访问基类中的成员在逻辑上可以把基类的成员看作一个整体来使用。1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数则必须在派生类构造函数的初始化列表阶段显示调用。2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。3.派生类的operator必须要调用基类的operator完成基类的复制。需要注意的是派生类的operator隐藏了基类的operator所以显示调用基类的operator需要指定基类作用域4.派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。5.派生类对象初始化先调用基类构造再调派生类构造。6.派生类对象析构清理先调用派生类析构再调基类的析构。// 基类人 class Person { public: // 基类构造函数初始化姓名创建Person对象时调用 Person(const char* name) :_name(name) { cout Person() endl; } // 基类拷贝构造函数用一个已存在的Person对象拷贝新建一个Person对象 Person(const Person p) { _name p._name; _sex p._sex; _age p._age; cout Person(const Person p) endl; } // 基类赋值运算符重载把一个Person对象的数据赋值给另一个已经存在的Person对象 Person operator(const Person p) { _name p._name; _sex p._sex; _age p._age; cout Person operator(const Person p) endl; return *this; } // 基类析构函数销毁Person对象时自动调用释放资源 ~Person() { cout ~Person() endl; } protected: string _name 张三; // 姓名protected派生类可以直接访问外部不能访问 string _sex; // 性别 int _age; // 年龄 }; // 派生类学生公有继承Person class Student : public Person { public: // 派生类构造函数 // 初始化列表先调用基类Person的构造初始化基类部分成员再初始化自己的学号_No Student(const char* name, int id2) :Person(name) ,_No(id) { } // 派生类拷贝构造函数 // 初始化列表调用基类Person的拷贝构造拷贝继承来的基类成员再拷贝派生类自己的_No // 这里Person(s)Student对象s向上转型传给Person的拷贝构造的const Person形参 Student(const Student s) :Person(s) ,_No(s._No) { } // 派生类赋值重载函数 Student operator(const Student s) { // 显式调用基类Person的赋值重载完成基类部分成员的赋值 Person::operator(s); // 赋值派生类自己新增的成员学号 _No s._No; return *this; } // 派生类析构函数 // 派生类析构执行完后编译器会自动调用基类析构这里手动写Person::~Person()是多余的仅演示原理 ~Student() { Person::~Person(); } private: int _No; // 学号派生类自己独有的成员基类Person没有 };4.2实现一个不能被继承的类方法一在类的定义讲到继承的方式和访问方式如果基类的构造函数定义成私有就不能在派生类调用而在派生类构造是必须调用基类的构造那么派生类就无法实例化出对象。方法二c11 后增加了一个新的关键字final,final修饰基类派生类就不能继承了。//c 11 class Base final { public: void func5() { cout Base::func5 endl; } protected: int a 1; private: //Base() //{} }; class Derive :public Base { void func4() { cout Derive::func4 endl; } protected: int b 2; };5.继承和友元友元关系不能被继承。基类的友元函数仅拥有访问基类内部成员的权限不会自动成为派生类的友元如果要让同一个友元函数访问派生类的私有成员必须在派生类中单独声明该函数为友元。class Student; class Person { public: friend void Display(const Person p, const Student s); protected: string _name; }; class Student : public Person // Student公有继承Person { private: int _num; }; void Display(const Person p, const Student s) { cout p._name endl; cout s._num endl; //编译报错Display只是Person友元不是Student友元访问不了_num }6.继承和静态成员基类定义了static静态成员则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类都只有一个static成员实例。可以通过代码验证一下#include iostream #include string using namespace std; class Person { public: string _name; static int _count; // 静态成员属于整个Person类不属于某个对象 }; // 静态成员变量必须在类外定义初始化 int Person::_count 0; class Student : public Person { protected: int _stuNum; }; int main() { Person p; Student s; cout p._name endl; cout s._name endl; // 静态成员属于Person类只有一份内存所有对象共享 cout p._count endl; cout s._count endl; // 公有继承下派生类可以通过类域访问基类的静态成员 cout Person::_count endl; cout Student::_count endl; return 0; }vs下运行结果总结普通成员随对象一份对象一份拷贝静态成员属于基类只有一份继承不会生成副本派生类只是有权限去访问它7.多继承及其菱形继承问题7.1继承模型单继承一个派生类只有一个直接基类时称这个继承关系为单继承多继承一个派生类有两个或以上直接基类时称这个继承关系为多继承多继承对象在内存中的模型是先继承的基类在前面后面继承的基类在后面派生类成员在放到最后面。菱形继承菱形继承是多继承的一种特殊情况。菱形继承的问题从下面的对象成员模型构造可以看出菱形继承有数据冗余和二义性的问题。可以看下面模型图理解一下7.2虚继承菱形继承的数据冗余和二义性是因为相当继承两份相同的类那有没有什么办法做到只继承一份虚继承就可以做到。在中间两层子类Student、Teacher继承 Person 的时候加上virtual变成虚继承。class Person {}; class Student : virtual public Person{}; // 虚继承 class Teacher : virtual public Person{}; // 虚继承 class Assistant : public Student, public Teacher{};Assistant对象只保留一份 Person 基类不再重复拷贝消除冗余同时解决访问二义性因为虚继承不会在子类里面直接嵌入父类成员而是用虚基表指针指向唯一的公共虚基类 Person。注意virtual是加在谁去继承顶层的公共的父类的继承列表上。8.继承和组合public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。组合是一种haS-a的关系。假设B组合了A每个B对象中都有一个A对象。继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-boxreuse)。术语“白箱”是相对可视性而言在继承方式中基类的内部细节对派生类可见。继承一定程度破坏了基类的封装基类的改变对派生类有很大的影响。派生类和基类间的依赖关系很强耦合度高。对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复black-boxreuse因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系耦合度低。优先使用对象组合有助于你保持每个类被封装。优先使用组合而不是继承。实际尽量多去用组合组合的耦合度低代码维护性好。不过也不太那么绝对类之间的关系就适合继承is-a那就用继承另外要实现多态也必须要继承。类之间的关系既适合用继承(is-a也适合组合has-a就用组合。以上就是cpp继承相关的知识点。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Linux内核模块机制原理与驱动开发实战 2026/9/12 21:55:26

Linux内核模块机制原理与驱动开发实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
CMSIS-6:嵌入式开发的源码静态工程范式重构 2026/9/12 21:55:26

CMSIS-6:嵌入式开发的源码静态工程范式重构

1. CMSIS-6不是升级补丁,而是嵌入式开发范式的结构性重置CMSIS-6这个编号本身就有误导性。很多人第一反应是“CMSIS-5的下一个版本”,就像Linux内核从5.x升到6.x那样平滑过渡。但实际完全不是——CMSIS-6是一次彻底推倒重来的架构重构,它不再…

阅读更多 →
问数Agent架构设计:从自然语言到可信数据结论的四层链路 2026/9/12 21:55:26

问数Agent架构设计:从自然语言到可信数据结论的四层链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Simulink冷热电三联供系统建模与仿真实践 2026/9/12 21:55:26

Simulink冷热电三联供系统建模与仿真实践

1. 冷热电三联供系统仿真概述冷热电三联供(Combined Cooling, Heating and Power,简称CCHP)系统是一种高效的综合能源利用方案。它通过一次能源的梯级利用,同时满足用户的电力、热能和冷能需求,整体能源利用率可达70%以…

阅读更多 →
ToF相机全链路解析:从VCSEL到深度图像应用 2026/9/12 21:55:26

ToF相机全链路解析:从VCSEL到深度图像应用

做了几年 ToF 相机,从最早把模组焊在调试板上看 RAW 图,到后来负责整机集成,再到帮客户调上层算法,我最大的感受是:这门技术最大的门槛不是某一个环节很难,而是链路太长,知识太碎。搞硬件的只聊…

阅读更多 →
BERT中文文本分类实战:从训练到HTTP接口部署 2026/9/12 21:52:26

BERT中文文本分类实战:从训练到HTTP接口部署

简介:一套基于BERT模型的深度学习中文文本分类项目,面向计算机相关专业学生、毕业设计开发者及NLP入门者,解决中文文本分类任务从模型训练到服务部署的完整流程需求。压缩包共18个文件,核心由11个Python脚本构成,涵盖数…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞