当前位置: 首页 > article >正文

C++面向对象编程之继承

目录一、继承的概念及定义1.1 继承的基本概念1.2 继承的定义与访问方式1.2.1 定义格式1.2.2 继承方式与访问权限1.3 继承类模板二、基类与派生类的转换2.1 向上转型Upcasting2.2 向下转型Downcasting三、 继承中的作用域3.1 隐藏规则3.2 函数隐藏四、派生类的默认成员函数4.1 六大默认成员函数在继承中的行为4.2 代码示例4.3 实现不能被继承的类五、继承与友元六、 继承与静态成员七、多继承与菱形继承7.1 继承模型7.2 虚继承7.3 IO库中的菱形虚拟继承八、继承与组合8.1 关系类型比较8.2 选择原则九、总结一、继承的概念及定义1.1 继承的基本概念继承Inheritance)是面向对象程序设计OOP中最核心的机制之一它允许我们基于已有类创建新类实现代码复用和功能扩展。通过继承派生类子类可以自动获得基类父类的属性和方法同时可以添加自己特有的成员。没有继承时的代码冗余问题class Student { public: void identity() { /* ... */ } void study() { /* ... */ } protected: string _name peter; string _address; string _tel; int _age 18; int _stud; // 学号 }; class Teacher { public: void identity() { /* ... */ } void teaching() { /* ... */ } protected: string _name 张三; int _age 18; string _address; string _tel; string _title; // 职称 };上述代码中Student和Teacher类有许多相同的成员变量和函数造成了代码冗余。使用继承优化后的代码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 _stud; // 学号 }; class Teacher : public Person { public: void teaching() { /* ... */ } protected: string _title; // 职称 };1.2 继承的定义与访问方式1.2.1 定义格式下面我们看到Person是基类也称作父类。Student是派生类也称作子类。1.2.2 继承方式与访问权限继承方式有三种public、protected和private。不同继承方式下基类成员在派生类中的访问权限变化如下基类成员/继承方式public继承protected继承private继承public成员publicprotectedprivateprotected成员protectedprotectedprivateprivate成员不可见不可见不可见重要规则基类private成员在派生类中不可见但仍被继承访问权限计算Min(成员在基类的访问限定符继承方式)class默认private继承struct默认public继承实践中主要使用public继承// 实例演⽰三种继承关系下基类成员的各类型成员访问关系的变化 class Person { public : void Print () { cout_name endl; } protected : string _name ; // 姓名 private : int _age ; // 年龄 }; //class Student : protected Person //class Student : private Person class Student : public Person { protected : int _stunum ; // 学号 };1.3 继承类模板namespace bit { templateclass T class stack : public std::vectorT { public: void push(const T x) { std::vectorT::push_back(x); } void pop() { std::vectorT::pop_back(); } const T top() { return std::vectorT::back(); } bool empty() { return std::vectorT::empty(); } }; }二、基类与派生类的转换2.1 向上转型Upcastingpublic继承的派生类对象可以赋值给基类的指针/基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中基类那部分切出来基类指针或引用指向的是派生类中切出来的基类那部分。class Person { protected: string _name; string _sex; int _age; }; class Student : public Person { public: int _No; // 学号 }; int main() { Student sobj; // 派生类对象赋值给基类指针/引用 Person* p sobj; Person rp sobj; // 派生类对象赋值给基类对象 Person pobj sobj; // 错误基类对象不能赋值给派生类对象 // sobj pobj; return 0; }2.2 向下转型Downcasting基类指针或引用可以通过强制类型转换赋值给派生类的指针或引用但需要注意安全性。三、 继承中的作用域3.1 隐藏规则在继承体系中基类和派生类都有独立的作用域。派生类和基类中有同名成员派生类成员将屏蔽基类对同名成员的直接访问这种情况叫隐藏。在派生类成员函数中可以使用基类::基类成员 显示访问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; // 访问派生类的_num } protected: int _num 999; // 学号隐藏了基类的_num };3.2 函数隐藏需要注意的是如果是成员函数的隐藏只需要函数名相同就构成隐藏。在实际中在继承体系里面最好不要定义同名的成员。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; } }; int main() { B b; b.fun(10); // 正确 // b.fun(); // 错误被隐藏 b.A::fun(); // 正确显式调用 return 0; }四、派生类的默认成员函数4.1 六大默认成员函数在继承中的行为6个默认成员函数默认的意思就是指我们不写编译器会帮我们自动生成一个那么在派生类中这几个成员函数是如何生成的呢构造函数必须调用基类构造函数初始化基类部分拷贝构造函数必须调用基类拷贝构造完成基类部分的拷贝赋值运算符必须调用基类赋值运算符完成基类部分的赋值析构函数完成后自动调用基类析构函数取地址运算符通常使用编译器生成版本const取地址运算符通常使用编译器生成版本因为多态中一些场景析构函数需要构成重写重写的条件之一是函数名相同(这个我们多态章节会讲解。那么编译器会对析构函数名进行特殊处理处理成destructor()所以基类析构函数不加virtual的情况下派生类析构函数和基类析构函数构成隐藏关系。4.2 代码示例class Person { public: Person(const char* name peter) : _name(name) { cout Person() endl; } Person(const Person p) : _name(p._name) { cout Person(const Person p) endl; } Person operator(const Person p) { cout Person operator(const Person p) endl; if (this ! p) _name p._name; return *this; } ~Person() { cout ~Person() endl; } protected: string _name; }; class Student : public Person { public: Student(const char* name, int num) : Person(name), _num(num) { cout Student() endl; } Student(const Student s) : Person(s), _num(s._num) { cout Student(const Student s) endl; } Student operator(const Student s) { cout Student operator(const Student s) endl; if (this ! s) { Person::operator(s); // 显式调用基类operator _num s._num; } return *this; } ~Student() { cout ~Student() endl; } protected: int _num; };4.3 实现不能被继承的类方法一C98方式构造函数私有化基类的构造函数私有派生类的构成必须调用基类的构造函数但是基类的构造函数私有化以后派生类看不见就不能调用了。那么派生类就无法实例化出对象。class NonInheritable { private: NonInheritable() {} // 私有构造函数 }; class Derived : public NonInheritable { // 错误无法访问私有构造函数 };方法二C11方式使用final关键字class Base final { // 使用final关键字 public: void func() { cout Base::func endl; } }; class Derive : public Base { // 错误不能继承final类 };五、继承与友元友元关系不能继承基类的友元不能访问派生类的私有和保护成员。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; // 错误不是Student的友元 } // 解决方案让Display也成为Student的友元 class Student : public Person { friend void Display(const Person p, const Student s); protected: int _stuNum; };六、 继承与静态成员基类定义了static静态成员则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类都只有一个static成员实例。class Person { public: string _name; static int _count; // 静态成员 }; 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; // 不同地址 cout p._count endl; // 相同地址 cout s._count endl; // 相同地址 cout Person::_count endl; // 相同值 cout Student::_count endl; // 相同值 return 0; }七、多继承与菱形继承7.1 继承模型单继承一个派生类只有一个直接基类时称这个继承关系为单继承。多继承一个派生类有两个或以上直接基类时称这个继承关系为多继承。多继承对象在内存中的模型是先继承的基类在前面后面继承的基类在后面派生类成员放到最后面。菱形继承菱形继承是多继承的一种特殊情况。菱形继承的问题从下面的对象成员模型构造可以看出菱形继承有数据冗余和二义性的问题在Assistant的对象中Person成员会有两份。支持多继承就一定会有菱形继承像Java就直接不支持多继承规避掉了这里的问题所以实践中我们也是不建议设计出菱形继承这样的模型的。class Person { public: string _name; // 姓名 }; class Student : public Person { protected: int _num; //学号 }; class Teacher : public Person { protected: int _id; // 职⼯编号 }; class Assistant : public Student, public Teacher { protected: string _majorCourse; // 主修课程 }; int main() { // 编译报错error C2385: 对“_name”的访问不明确 Assistant a; a._name peter; // 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题但是数据冗余问题⽆法解决 a.Student::_name xxx; a.Teacher::_name yyy; return 0; }7.2 虚继承很多人说C语法复杂其实多继承就是一个体现。有了多继承就存在菱形继承有了菱形继承就有菱形虚继承底层实现就很复杂性能也会有一些损失。所以最好不要设计出菱形继承。多继承可以认为是C的缺陷之一后来的一些编程语言大多没有多继承如Java。class Person { public: string _name; }; class Student : virtual public Person { // 虚继承 protected: int _num; }; class Teacher : virtual public Person { // 虚继承 protected: int _id; }; class Assistant : public Student, public Teacher { protected: string _majorCourse; }; int main() { Assistant a; a._name peter; // 正确无二义性且无数据冗余 return 0; }我们可以设计出多继承但是不建议设计出菱形继承因为菱形虚拟继承以后。⽆论是使⽤还是底层逻辑都会复杂很多。当然有多继承语法支持就⼀定存在会设计出菱形继承的情况像Java是不支持多继承的就避开了菱形继承。7.3 IO库中的菱形虚拟继承templateclass CharT, class Traits std::char_traitsCharT class basic_ostream : virtual public std::basic_iosCharT, Traits {}; templateclass CharT, class Traits std::char_traitsCharT class basic_istream : virtual public std::basic_iosCharT, Traits {};八、继承与组合8.1 关系类型比较继承is-a关系派生类是基类的一种特殊类型组合has-a关系类中包含另一个类的对象作为成员8.2 选择原则优先使用组合而不是继承只有真正的is-a关系才使用继承需要实现多态时必须使用继承组合耦合度低更易于维护// 组合示例 class Tire { protected: string _brand Michelin; size_t _size 17; }; class Car { protected: string _colour 白色; string _num 陕ABIT00; Tire _t1, _t2, _t3, _t4; // 组合关系 }; // 继承示例 class BMW : public Car { // is-a关系 public: void Drive() { cout 好开-操控 endl; } }; class Benz : public Car { // is-a关系 public: void Drive() { cout 好坐-舒适 endl; } };九、总结继承是C面向对象编程的核心机制之一正确理解和使用继承对于编写高质量、可维护的代码至关重要。本文详细介绍了继承的各个方面包括基本概念、访问控制、作用域规则、成员函数特性、多继承问题以及继承与组合的选择原则。在实际开发中应遵循以下最佳实践优先使用public继承尽量避免多继承和菱形继承优先选择组合而不是继承注意继承中的隐藏规则和作用域问题正确处理派生类中的默认成员函数通过深入理解这些概念和原则开发者可以更好地利用C的继承机制编写出更加健壮和可维护的面向对象程序。参考资料Stanley B. Lippman, C PrimerScott Meyers, Effective CBjarne Stroustrup, The C Programming Language

相关文章:

C++面向对象编程之继承

目录 一、继承的概念及定义 1.1 继承的基本概念 1.2 继承的定义与访问方式 1.2.1 定义格式 1.2.2 继承方式与访问权限 1.3 继承类模板 二、基类与派生类的转换 2.1 向上转型(Upcasting) 2.2 向下转型(Downcasting) 三、…...

QMCDecode:让QQ音乐加密音频在Mac上自由播放

QMCDecode:让QQ音乐加密音频在Mac上自由播放 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默认转换结果存…...

WarcraftHelper实用指南:优化魔兽争霸3在现代系统上的游戏体验

WarcraftHelper实用指南:优化魔兽争霸3在现代系统上的游戏体验 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 魔兽争霸3作为一款经典即时…...

分布式爬虫平台架构设计:从权限控制到规模化数据采集实战

1. 项目概述:从“权限实验室”到“爬虫农场”的构想最近在GitHub上看到一个挺有意思的项目,叫“claw-farm”,来自一个叫“PermissionLabs”的组织。光看这个名字,就让人忍不住想点进去看看。PermissionLabs,直译是“权…...

Sunshine游戏串流服务器完整指南:15分钟搭建你的私人云游戏平台

Sunshine游戏串流服务器完整指南:15分钟搭建你的私人云游戏平台 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine Sunshine是一款免费开源的自托管游戏串流服务器&#x…...

Yalmip进阶技巧:用recover和see函数‘逆向工程’调试你的优化模型

Yalmip高阶调试:用recover和see函数透视优化模型内部结构 当你的Yalmip模型输出结果与预期不符时,那种挫败感就像面对一个黑箱——明明输入了正确的公式,却得到难以解释的解。本文将揭示两个被低估的调试利器:recover和see函数&am…...

FIR威胁情报集成:如何利用YETI框架增强事件响应能力

FIR威胁情报集成:如何利用YETI框架增强事件响应能力 【免费下载链接】FIR Fast Incident Response 项目地址: https://gitcode.com/gh_mirrors/fi/FIR FIR(Fast Incident Response)作为一款高效的事件响应工具,通过与YETI威…...

超越基础教程:用ROS USB_cam功能包和cv_bridge打造你的简易视频监控与处理流水线

超越基础教程:用ROS USB_cam功能包和cv_bridge打造你的简易视频监控与处理流水线 在机器人操作系统(ROS)的生态中,USB_cam功能包常被视为入门级工具,但它的潜力远不止于简单的图像采集。本文将带你从零构建一个完整的视…...

程序员搞钱新思路:在 RapidAPI 上变现你的代码资产

在这个万物互联的时代,很多开发者在业余时间写出的有趣接口、爬虫工具或是数据处理服务,往往在完成自己的需求后就被束之高阁。其实,这些沉睡在硬盘里的代码,完全有机会成为我们的“被动收入”来源。今天,我们就来聊聊…...

OpenClaw 2.6.6 Windows 环境配置与服务启动详解

OpenClaw 2.6.6 Windows 一键部署教程|零基础搭建本地 AI 智能助手 OpenClaw(小龙虾)是一款可在本地环境运行的 AI 智能操作工具,能够通过自然语言指令完成电脑操控、文件管理、办公自动化、浏览器操作、数据整理等任务。全程可视…...

AngularJS自定义指令开发终极指南:构建可复用组件的最佳实践

AngularJS自定义指令开发终极指南:构建可复用组件的最佳实践 【免费下载链接】angular-app Reference application for AngularJS 项目地址: https://gitcode.com/gh_mirrors/an/angular-app AngularJS作为一款经典的前端框架,其自定义指令功能为…...

LLMs-from-scratch-CN实战案例:构建垃圾邮件分类器与用户界面

LLMs-from-scratch-CN实战案例:构建垃圾邮件分类器与用户界面 【免费下载链接】LLMs-from-scratch-CN LLMs-from-scratch项目中文翻译 项目地址: https://gitcode.com/gh_mirrors/llm/LLMs-from-scratch-CN LLMs-from-scratch-CN是一个优秀的开源项目&#x…...

京东自动化抢购工具终极指南:3步轻松实现智能抢单

京东自动化抢购工具终极指南:3步轻松实现智能抢单 【免费下载链接】jd-assistant 京东抢购助手:包含登录,查询商品库存/价格,添加/清空购物车,抢购商品(下单),查询订单等功能 项目地址: https://gitcode.…...

独立开发者如何借助Taotoken为产品集成灵活的AI能力

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 独立开发者如何借助Taotoken为产品集成灵活的AI能力 为SaaS产品添加智能对话或内容生成功能,已成为许多独立开发者提升…...

移动端N8N管理工具Nathan:React Native构建的自动化运维利器

1. 项目概述:Nathan,你的移动端N8N控制中心如果你和我一样,是个重度自动化爱好者,把N8N当作数字世界的“瑞士军刀”,那你肯定也遇到过这样的场景:正躺在沙发上,突然想起有个工作流需要紧急触发&…...

Markdownlint核心架构解析:深入理解Ruby实现的代码检查引擎

Markdownlint核心架构解析:深入理解Ruby实现的代码检查引擎 【免费下载链接】markdownlint Markdown lint tool 项目地址: https://gitcode.com/gh_mirrors/mar/markdownlint Markdownlint是一款基于Ruby开发的Markdown代码检查工具,通过灵活的规…...

gta侠盗猎车手5 2026最新绿色破解版免费下载 pc版 手机版通用

下载链接(点击跳转) 在数字娱乐的历史长河中,很少有作品能像《侠盗猎车手5》(Grand Theft Auto V,简称GTA5)这样,跨越十余年、历经三个主机世代,依然稳居全球销量榜前列。它不仅是一…...

开源ChatGPT API Web界面部署指南:从React+Node.js架构到高级使用技巧

1. 项目概述:一个为ChatGPT API量身打造的开源Web界面如果你正在使用OpenAI的ChatGPT API进行开发,或者你是一个喜欢折腾、希望拥有一个更灵活、更可控的聊天交互界面的用户,那么你很可能已经厌倦了官方Web界面那有限的定制能力,或…...

HoRain云-PHP循环优化:提升性能的5个关键技巧

🎬 HoRain 云小助手:个人主页 ⛺️生活的理想,就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。 目录 ⛳️ 推荐 …...

别再死记硬背VisionPro工具了!用这3个真实工业项目(缺陷检测/尺寸测量/机器人抓取)带你实战入门

VisionPro实战:3个工业级机器视觉项目从零到落地 在机器视觉领域,理论知识固然重要,但真正的技能提升往往来自于解决实际问题的过程。VisionPro作为工业视觉领域的标杆软件,其强大功能需要通过真实场景才能充分释放。本文将带你跳…...

在数据预处理与分析场景中集成大模型API的实践思路

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在数据预处理与分析场景中集成大模型API的实践思路 对于数据工程师而言,处理海量非结构化文本数据是一项日常工作。无论…...

Taotoken 的容灾与路由机制保障了业务连续性

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken 的容灾与路由机制保障了业务连续性 在依赖外部大模型服务的业务开发中,服务稳定性是核心关切之一。上游服务偶…...

高级MoveIt编程技巧:自定义运动规划器和优化算法的实现方法

高级MoveIt编程技巧:自定义运动规划器和优化算法的实现方法 【免费下载链接】moveit :robot: The MoveIt motion planning framework 项目地址: https://gitcode.com/gh_mirrors/mo/moveit MoveIt是一个强大的机器人运动规划框架,为开发者提供了灵…...

基于MCP协议与Ledger Connect构建安全的加密资产AI助手

1. 项目概述与核心价值最近在折腾AI智能体开发,特别是想给Claude Desktop这类工具增加点“超能力”,让它能直接读取我的财务数据,帮我分析月度开支或者规划预算。这个需求听起来简单,但实际操作起来,你会发现一个核心痛…...

AI产品经理必学:从业务小白到大模型高手的“前后左右”能力模型(收藏版)

文章为AI产品经理提供了从业务到技术落地的能力提升框架,分为“前后左右”四个维度:数据层(SQL、数据清洗、向量数据库)、AI核心层(提示词工程、RAG、Agent、模型评估)、后端/架构层(API接口设计…...

lm-format-enforcer正则表达式解析:完整语法支持与实战案例

lm-format-enforcer正则表达式解析:完整语法支持与实战案例 【免费下载链接】lm-format-enforcer Enforce the output format (JSON Schema, Regex etc) of a language model 项目地址: https://gitcode.com/gh_mirrors/lm/lm-format-enforcer lm-format-enf…...

3步开启OBS专业直播:RTSP服务器插件实战指南

3步开启OBS专业直播:RTSP服务器插件实战指南 【免费下载链接】obs-rtspserver RTSP server plugin for obs-studio 项目地址: https://gitcode.com/gh_mirrors/ob/obs-rtspserver 你是否曾希望将OBS的直播内容直接推送到监控系统、智能电视或专业视频设备&am…...

Scroll Reverser终极指南:3步解决macOS多设备滚动混乱

Scroll Reverser终极指南:3步解决macOS多设备滚动混乱 【免费下载链接】Scroll-Reverser Per-device scrolling prefs on macOS. 项目地址: https://gitcode.com/gh_mirrors/sc/Scroll-Reverser 还在为Mac上触控板和鼠标的滚动方向相反而抓狂吗?S…...

5月19日Fitbit应用更名Google Health,功能升级、隐私有保障,高级版费用调整

Fitbit应用重大改版周四,于2021年完成对Fitbit收购的谷歌宣布,Fitbit应用程序即将迎来重大改版,甚至连名字都将改变,它将于5月19日更名为Google Health。谷歌产品管理总监泰勒赫尔格伦(Taylor Helgren)对CN…...

终极跨平台Unity资源提取教程:5分钟学会AssetRipper完整使用指南

终极跨平台Unity资源提取教程:5分钟学会AssetRipper完整使用指南 【免费下载链接】AssetRipper GUI Application to work with engine assets, asset bundles, and serialized files 项目地址: https://gitcode.com/GitHub_Trending/as/AssetRipper AssetRip…...