C++中的继承

继承是 C++ 面向对象三大特性(封装、继承、多态)的核心,核心价值是代码复用和层次化类设计。本文全面覆盖继承的语法、对象模型、构造析构、同名成员处理、多继承及菱形继承等关键知识点,并附注意事项和示例。
一、继承的基本语法

  1. 语法格式
// 基类(父类):被继承的类class 基类名 {// 成员(属性、方法)};// 派生类(子类):继承基类class 派生类名 : 继承方式 基类名 {// 子类扩展的成员(可新增/重写)};
  1. 核心概念
    基类(父类):提供通用属性 / 方法的类(如Person);
    派生类(子类):复用基类成员,同时扩展自身功能的类(如Student);
    继承的本质:子类拥有基类的所有成员(private成员虽不可直接访问,但仍占用内存)。
  2. 基础示例
#include<iostream>#include<string>usingnamespace std;// 基类:PersonclassPerson{public: string name;int age;voidshowInfo(){ cout <<"姓名:"<< name <<",年龄:"<< age << endl;}};// 派生类:Student(public继承Person)classStudent:publicPerson{public:int studentId;// 子类扩展属性voidshowStudentInfo(){// 复用基类成员 cout <<"姓名:"<< name <<",年龄:"<< age <<",学号:"<< studentId << endl;}};intmain(){ Student s; s.name ="张三";// 访问基类public成员 s.age =20; s.studentId =1001; s.showInfo();// 调用基类方法 s.showStudentInfo();// 调用子类方法return0;}

二、继承方式(3 种)
继承方式决定基类成员在子类中的访问权限,C++ 支持 3 种继承方式,默认是private。

  1. 关键规则
    基类private成员:无论哪种继承方式,子类都不可直接访问(可通过基类public/protected方法间接访问);
    继承方式仅修改 “基类成员在子类中的访问权限”,不改变基类自身的权限;
    实际开发中优先使用public继承(避免权限过度收缩,符合代码可读性)。
  2. 示例:不同继承方式的权限差异

权限对照表

在这里插入图片描述
classBase{public:int pub;protected:int pro;private:int pri;};// public继承classPubDerive:publicBase{public:voidtest(){ pub =1;// 合法(public→public) pro =2;// 合法(protected→protected)// pri = 3; // 非法(基类private不可访问)}};// private继承classPriDerive:privateBase{public:voidtest(){ pub =1;// 合法(public→private) pro =2;// 合法(protected→private)}};intmain(){ PubDerive pd; pd.pub =10;// 合法(public继承后仍为public)// pd.pro = 20; // 非法(protected) PriDerive prd;// prd.pub = 10; // 非法(private继承后变为private)return0;}

三、继承中的对象模型

  1. 核心结论
    子类对象的内存 = 基类成员(包括private,仅不可直接访问) + 子类自身成员;
    编译器会将基类的所有成员(无论权限)都继承到子类内存中,private成员只是被隐藏,并非不存在。
  2. 示例:验证对象模型(通过sizeof)
#include<iostream>usingnamespace std;classBase{public:int a;protected:int b;private:int c;// 虽不可访问,但占用内存};classDerive:publicBase{public:int d;};intmain(){// Base:int a + int b + int c = 12字节(3*4)// Derive:Base(12) + int d = 16字节 cout <<"Base大小:"<<sizeof(Base)<< endl;// 输出:12 cout <<"Derive大小:"<<sizeof(Derive)<< endl;// 输出:16return0;}
  1. 内存布局(简化)
    Derive对象内存:[a (public)] → [b (protected)] → [c (private)] → [d (public)]

四、继承中的构造和析构函数

  1. 核心规则
    构造函数 / 析构函数不可继承,但子类构造时会自动调用基类构造,析构时自动调用基类析构;
    调用顺序:
    构造:基类构造 → 子类构造(先初始化父类部分,再初始化子类部分);
    析构:子类析构 → 基类析构(先清理子类部分,再清理父类部分);
    若基类无默认构造(仅带参构造),子类需在初始化列表显式调用基类构造。
  2. 示例 1:默认构造的调用
#include<iostream>usingnamespace std;classBase{public:Base(){ cout <<"Base构造函数"<< endl;}~Base(){ cout <<"Base析构函数"<< endl;}};classDerive:publicBase{public:Derive(){ cout <<"Derive构造函数"<< endl;}~Derive(){ cout <<"Derive析构函数"<< endl;}};intmain(){ Derive d;// 输出顺序:Base构造 → Derive构造 → Derive析构 → Base析构return0;}
  1. 示例 2:显式调用基类带参构造
#include<iostream>usingnamespace std;classBase{public:int a;// 基类只有带参构造,无默认构造Base(int a_):a(a_){ cout <<"Base带参构造:a="<< a << endl;}};classDerive:publicBase{public:int b;// 子类初始化列表:先调用基类带参构造,再初始化自身成员Derive(int a_,int b_):Base(a_),b(b_){ cout <<"Derive带参构造:b="<< b << endl;}};intmain(){ Derive d(10,20);// 输出:Base带参构造:a=10 → Derive带参构造:b=20return0;}

五、继承中的同名成员处理
当子类与基类有同名成员(属性 / 方法)时,默认访问子类成员,需通过作用域解析符:: 访问基类同名成员。

  1. 同名属性
#include<iostream>usingnamespace std;classBase{public:int num =100;};classDerive:publicBase{public:int num =200;// 同名属性voidshow(){ cout <<"子类num:"<< num << endl;// 访问子类:200 cout <<"基类num:"<< Base::num << endl;// 访问基类:100}};intmain(){ Derive d; d.show();// 外部访问:默认子类,基类需加作用域 cout <<"外部访问子类num:"<< d.num << endl;// 200 cout <<"外部访问基类num:"<< d.Base::num << endl;// 100return0;}
  1. 同名方法
#include<iostream>usingnamespace std;classBase{public:voidfunc(){ cout <<"Base::func()"<< endl;}voidfunc(int a){ cout <<"Base::func(int):"<< a << endl;}// 重载};classDerive:publicBase{public:voidfunc(){ cout <<"Derive::func()"<< endl;}// 重写};intmain(){ Derive d; d.func();// 调用子类:Derive::func()// d.func(10); // 非法!子类重写后,基类重载的func被隐藏 d.Base::func(10);// 显式调用基类重载方法:Base::func(int):10return0;}

关键注意
子类重写基类同名方法后,基类的所有重载版本都会被隐藏(需显式加作用域访问)。
六、继承中的同名静态成员处理
静态成员属于类(而非对象),同名处理规则与普通成员一致,但有 2 种访问方式(对象 / 类名)。
示例:同名静态成员

#include<iostream>usingnamespace std;classBase{public:staticint num;staticvoidfunc(){ cout <<"Base::func()"<< endl;}};int Base::num =100;// 静态成员类外初始化classDerive:publicBase{public:staticint num;staticvoidfunc(){ cout <<"Derive::func()"<< endl;}};int Derive::num =200;intmain(){// 方式1:通过对象访问 Derive d; cout <<"子类num(对象):"<< d.num << endl;// 200 cout <<"基类num(对象):"<< d.Base::num << endl;// 100// 方式2:通过类名访问(推荐,静态成员专属) cout <<"子类num(类名):"<< Derive::num << endl;// 200 cout <<"基类num(类名):"<< Derive::Base::num << endl;// 100// 静态方法Derive::func();// 子类:Derive::func() Derive::Base::func();// 基类:Base::func()return0;}

七、多继承语法
多继承指子类同时继承多个基类,语法简单但易引发二义性,实际开发需慎用。

  1. 语法格式
class 子类名 : 继承方式1 基类1, 继承方式2 基类2,...{// 子类成员};
  1. 示例:多继承基础
#include<iostream>usingnamespace std;classBase1{public:int a =10;voidfunc1(){ cout <<"Base1::func1()"<< endl;}};classBase2{public:int a =20;// 与Base1同名voidfunc2(){ cout <<"Base2::func2()"<< endl;}};// 多继承:public继承Base1和Base2classDerive:publicBase1,publicBase2{public:int b =30;};intmain(){ Derive d; d.func1();// 调用Base1:无歧义 d.func2();// 调用Base2:无歧义// cout << d.a << endl; // 非法!二义性(Base1::a 和 Base2::a) cout << d.Base1::a << endl;// 10(指定基类,解决二义性) cout << d.Base2::a << endl;// 20return0;}
  1. 多继承的二义性解决
    同名成员:通过基类名::成员名指定访问的基类;
    若多个基类继承自同一祖先(菱形继承),需用虚继承解决(见下文)。

八、菱形继承(钻石继承)

  1. 定义
    子类间接继承同一个基类(如A → B、A → C、B → D、C → D),导致D拥有A的两份拷贝,引发二义性和内存冗余。
  2. 问题示例(未用虚继承)
#include<iostream>usingnamespace std;// 顶层基类classA{public:int num =10;};// 中间基类B:继承AclassB:publicA{};// 中间基类C:继承AclassC:publicA{};// 最终子类D:多继承B和CclassD:publicB,publicC{};intmain(){ D d;// cout << d.num << endl; // 非法!二义性(B::A::num 和 C::A::num) cout << d.B::num << endl;// 10 cout << d.C::num << endl;// 10(两份A的拷贝,冗余)return0;}
  1. 解决:虚继承(virtual)
    语法:中间基类继承顶层基类时,加virtual关键字;
    效果:让最终子类只保留一份顶层基类的拷贝(共享基类成员),消除二义性和冗余。
  2. 虚继承示例
#include<iostream>usingnamespace std;// 顶层基类classA{public:int num =10;};// 中间基类:虚继承A(关键)classB:virtualpublicA{};classC:virtualpublicA{};// 最终子类:多继承B和CclassD:publicB,publicC{};intmain(){ D d; cout << d.num << endl;// 10(无歧义,仅一份A的拷贝) d.B::num =20; cout << d.C::num << endl;// 20(共享同一份num)return0;}
  1. 虚继承的底层
    虚继承会为中间基类添加虚基类指针(vbptr),指向虚基类表(vbtable),表中存储顶层基类成员的偏移量,确保最终子类只访问一份顶层基类成员。

九、继承的使用注意事项
1、优先使用 public 继承:protected/private 继承会收缩权限,导致代码扩展性差,仅特殊场景使用;
2、避免多继承:多继承易引发二义性,可通过 “组合” 替代(如子类中包含其他类的对象);
3、菱形继承必须用虚继承:否则会有二义性和内存冗余,虚继承是唯一解决方案;
4、构造函数的初始化列表:若基类无默认构造,子类必须显式调用基类带参构造;
5、不要重定义继承的非虚函数:易导致语义混乱,如需重写,应将基类函数声明为virtual(多态);
6、const 正确性:若子类调用基类的 const 方法,需保证自身方法的 const 修饰一致;
7、析构函数建议加 virtual:若通过基类指针删除子类对象,基类析构函数非 virtual 会导致子类析构不执行(内存泄漏);
8、避免继承模板类的实现细节:模板类的继承需关注实例化后的类型匹配,避免隐式转换问题;
9、基类 private 成员的访问:子类不可直接访问,需通过基类的 public/protected 方法间接访问,符合封装原则;
10、静态成员的继承:静态成员被所有子类共享,修改一个子类的静态成员会影响所有相关类。
十、总结
1、继承的核心是代码复用,但需平衡 “复用” 与 “耦合”:
2、单继承简单安全,优先使用;
3、多继承尽量避免,确需使用时注意二义性;
4、菱形继承必须用虚继承;
5、构造 / 析构、同名成员的处理是继承的高频考点,需熟练掌握作用域解析符的使用;
实际开发中,继承需配合多态(虚函数)使用,才能发挥面向对象的最大价值。

Read more

Flutter for OpenHarmony: Flutter 三方库 image_size_getter 零加载极速获取图片尺寸(鸿蒙 UI 布局优化必备)

Flutter for OpenHarmony: Flutter 三方库 image_size_getter 零加载极速获取图片尺寸(鸿蒙 UI 布局优化必备)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 应用布局时,我们经常遇到这样的挑战:为了防止 UI 抖动,需要在图片完全加载前预留一段占位空间。如果直接使用 Image.network 或 Image.file,直到图片解码完成前,我们都无法获知其宽高比。如果此时一次性加载大量高清大图,仅为了获取尺寸而消耗内存和流量,显然是不理智的。 image_size_getter 是一个极其聪明的库。它通过读取图片头部的少量二进制字节(通常只有几百字节),就能瞬间识别出 JPG、PNG、GIF、WebP 甚至 PSD 的原始尺寸。 一、核心原理图解 该库通过解析各种图片格式的 Header 结构实现免解码探测。 本地/网络图片文件 读取前 1KB 字节流 校验魔数

By Ne0inhk

6GB显存也能玩转2K AI绘画:腾讯混元Image-2.1 GGUF版部署完全指南

6GB显存也能玩转2K AI绘画:腾讯混元Image-2.1 GGUF版部署完全指南 【免费下载链接】hunyuanimage-gguf 项目地址: https://ai.gitcode.com/hf_mirrors/calcuis/hunyuanimage-gguf 你是否曾经因为显卡配置不够而放弃尝试AI绘画?现在,腾讯混元Image-2.1 GGUF版彻底改变了这一现状。这款革命性的图像生成模型通过先进的量化技术,将显存需求从原来的24GB大幅降至6GB级别,让普通消费级显卡也能流畅生成2K分辨率的高质量图像!🎨 为什么选择GGUF格式?三大优势解析 突破性的显存优化:传统AI绘画模型动辄需要12-16GB显存,而腾讯混元Image-2.1 GGUF版通过2-bit到8-bit的多精度量化方案,实现了50%以上的体积缩减。这意味着RTX 3060等主流显卡就能轻松驾驭专业级AI绘画。 模块化设计理念:采用"主模型+编码器+VAE"的分离式架构,用户可以根据自己的需求灵活组合: * 基础模型:hunyuanimage2.1标准版/轻量化版/V2精炼版 * 文本编

By Ne0inhk

VS Code+GitHub Copilot避坑指南:从安装配置到最佳实践的完整手册

VS Code + GitHub Copilot 深度驾驭手册:从避坑到精通的实战心法 如果你是一名 Visual Studio Code 的用户,并且对那个传说中能“读懂你心思”的 AI 编程伙伴 GitHub Copilot 感到好奇,甚至已经跃跃欲试,那么这篇文章就是为你准备的。我们不再重复那些泛泛而谈的“AI 将改变编程”的论调,而是直接切入核心:如何在你最熟悉的 VS Code 环境中,真正驯服 Copilot,让它从一个偶尔“胡言乱语”的助手,变成你编码流中如臂使指的高效组件。我们将聚焦于从安装配置的第一分钟开始,到融入你日常工作流的每一个细节,过程中你会遇到哪些真实的“坑”,以及如何优雅地跨过它们。这不是一篇简单的功能罗列,而是一份融合了配置技巧、心智模型和实战策略的深度指南。 1. 环境搭建与初始配置:奠定高效协作的基石 在兴奋地敲下第一行代码之前,一个稳固且经过优化的起点至关重要。许多初次使用者遇到的挫折,

By Ne0inhk