news 2026/8/30 2:59:23

一文带你深入了解隐藏的相关知识

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一文带你深入了解隐藏的相关知识

一文带你深入了解隐藏的相关知识


文章目录

  • 一文带你深入了解隐藏的相关知识
    • 一、什么是隐藏?
      • 最简单的例子
    • 二、隐藏的三种情况
      • 1. 成员变量的隐藏
      • 2. 成员函数的隐藏
      • 3. 重载函数的隐藏
    • 三、解决隐藏的方法
      • 方法1:使用using声明
      • 方法2:显式调用基类版本
    • 四、隐藏 vs 重写(覆盖)的区别
      • 详细解释
      • 代码运行结果
    • 五、隐藏的实际应用场景
      • 场景1:加强访问控制
      • 场景2:修改函数行为但保留接口
      • 场景3:静态多态(CRTP模式中的隐藏)
    • 六、常见陷阱和注意事项
      • 陷阱1:意外隐藏
      • 陷阱2:隐藏与重载的混淆
      • 最佳实践建议
    • 总结
  • 总结

好的,我来详细讲解C++继承中的**隐藏(Hiding)**机制。这是一个非常重要但也容易混淆的概念。

一、什么是隐藏?

隐藏是指:在派生类中定义了与基类同名的成员(函数或变量)时,基类的同名成员在派生类中变得"不可见"(被隐藏了)。

最简单的例子

#include<iostream>usingnamespacestd;classBase{public:voidfunc(){cout<<"Base::func()"<<endl;}};classDerived:publicBase{public:voidfunc(){// 隐藏了基类的funccout<<"Derived::func()"<<endl;}};intmain(){Derived d;d.func();// 输出:Derived::func()(基类的被隐藏)// 如果想调用基类的,必须指定作用域d.Base::func();// 输出:Base::func()return0;}

二、隐藏的三种情况

1. 成员变量的隐藏

classBase{public:intvalue=100;intsameName=10;};classDerived:publicBase{public:intvalue=200;// 隐藏了基类的valueintdifferent=20;voidprint(){cout<<"Derived value: "<<value<<endl;// 200(派生类的)cout<<"Base value: "<<Base::value<<endl;// 100(需要指定)cout<<"sameName: "<<sameName<<endl;// 10(基类的,没被隐藏)}};intmain(){Derived d;d.print();cout<<d.value<<endl;// 200(派生类的)cout<<d.Base::value<<endl;// 100(基类的)return0;}

2. 成员函数的隐藏

这是最容易混淆的地方,特别注意:隐藏不看参数,只看名字!

classBase{public:voidfunc(){cout<<"Base::func()"<<endl;}voidfunc(intx){cout<<"Base::func(int) x="<<x<<endl;}voidfunc(doublex){cout<<"Base::func(double) x="<<x<<endl;}};classDerived:publicBase{public:voidfunc(){// 隐藏了基类中所有名为func的函数cout<<"Derived::func()"<<endl;}};intmain(){Derived d;d.func();// ✅ 调用Derived::func()// d.func(10); // ❌ 错误!基类的func(int)被隐藏了// d.func(3.14); // ❌ 错误!基类的func(double)也被隐藏了// 必须指定基类作用域才能调用d.Base::func(10);// ✅ 这样才可以d.Base::func(3.14);// ✅ 这样才可以return0;}

3. 重载函数的隐藏

重要规则:派生类中定义同名函数会隐藏基类中所有同名函数(不管参数列表)

classBase{public:voidshow(){cout<<"Base::show()"<<endl;}voidshow(int){cout<<"Base::show(int)"<<endl;}voidshow(double){cout<<"Base::show(double)"<<endl;}};classDerived:publicBase{public:voidshow(){// 隐藏了基类所有的showcout<<"Derived::show()"<<endl;}};intmain(){Derived d;d.show();// ✅ 调用Derived的// d.show(10); // ❌ 被隐藏了// d.show(3.14); // ❌ 被隐藏了// 解决方案1:使用using声明return0;}

三、解决隐藏的方法

方法1:使用using声明

classBase{public:voidfunc(){cout<<"Base::func()"<<endl;}voidfunc(int){cout<<"Base::func(int)"<<endl;}};classDerived:publicBase{public:usingBase::func;// 把基类所有func函数引入派生类作用域voidfunc(){// 现在重载而不是隐藏cout<<"Derived::func()"<<endl;}};intmain(){Derived d;d.func();// Derived::func()d.func(10);// Base::func(int)(因为using声明)return0;}

方法2:显式调用基类版本

classDerived:publicBase{public:voidfunc(){cout<<"Derived::func()"<<endl;}voidcallBaseFunc(){Base::func();// 显式调用基类版本}voidcallBaseFunc(intx){Base::func(x);// 显式调用基类版本}};

四、隐藏 vs 重写(覆盖)的区别

这是初学者最容易混淆的,一定要分清楚:

特性隐藏重写(覆盖)
关键字virtual
函数名相同相同
参数可不同必须相同
返回值可不同必须相同(协变除外)
绑定方式静态绑定动态绑定
多态性
classBase{public:voidhide(){cout<<"Base::hide"<<endl;}// 会被隐藏virtualvoidoverride(){cout<<"Base::override"<<endl;}// 会被重写};classDerived:publicBase{public:voidhide(){cout<<"Derived::hide"<<endl;}// 隐藏voidoverride()override{cout<<"Derived::override"<<endl;}// 重写};intmain(){Base*ptr=newDerived();ptr->hide();// 静态绑定:Base::hide(隐藏不看指针实际类型)ptr->override();// 动态绑定:Derived::overridedeleteptr;return0;}

根据你提供的代码,ptr->hide();调用的是基类hide()函数。

虽然ptr指向的是一个Derived对象,但由于hide()不是虚函数,编译器根据指针的静态类型(即Base*)来决定调用哪个函数,这就是静态绑定

详细解释

  1. 指针类型ptr被声明为Base*类型,所以它的静态类型是Base*
  2. 函数类型hide()在基类中没有被声明为virtual,因此它是一个非虚函数。
  3. 绑定机制:对于非虚函数,编译器在编译时就根据指针的静态类型(Base*)确定了要调用的函数地址,所以它会调用Base::hide()
  4. 结果:因此,输出是"Base::hide"

override()是虚函数,所以调用它时会根据指针指向的对象的实际类型(Derived)进行动态绑定,输出"Derived::override"

代码运行结果

Base::hide // 来自静态绑定 Derived::override // 来自动态绑定

总结

  • 隐藏(hide):同名非虚函数,由指针/引用的静态类型决定调用哪个版本。
  • 重写(override):同名虚函数,由对象的实际类型决定调用哪个版本。

五、隐藏的实际应用场景

场景1:加强访问控制

classBase{public:voidpublicAPI(){cout<<"Base public API"<<endl;}};classDerived:privateBase{public:voidpublicAPI(){// 隐藏基类的publicAPIcout<<"Derived's own API"<<endl;Base::publicAPI();// 可以在内部使用基类的}};intmain(){Derived d;d.publicAPI();// 调用Derived的版本// d.Base::publicAPI(); // ❌ 不能直接调用基类的(private继承)return0;}

场景2:修改函数行为但保留接口

classLogger{public:voidlog(conststring&msg){cout<<"[LOG] "<<msg<<endl;}};classTimedLogger:publicLogger{public:voidlog(conststring&msg){// 隐藏基类的logstring timedMsg="["+getCurrentTime()+"] "+msg;Logger::log(timedMsg);// 调用基类版本完成实际工作}private:stringgetCurrentTime(){return"12:00:00";// 简化示例}};

场景3:静态多态(CRTP模式中的隐藏)

template<typenameDerived>classBase{public:voidinterface(){static_cast<Derived*>(this)->implementation();// 调用派生类版本}voidimplementation(){// 可能被隐藏cout<<"Base implementation"<<endl;}};classDerived:publicBase<Derived>{public:voidimplementation(){// 隐藏基类的implementationcout<<"Derived implementation"<<endl;}};

六、常见陷阱和注意事项

陷阱1:意外隐藏

classBase{public:virtualvoidprocess(intx){cout<<"Base::process(int)"<<endl;}};classDerived:publicBase{public:// 本意是想重写,但参数不同变成了隐藏virtualvoidprocess(doublex){// ❌ 隐藏了基类的process(int)cout<<"Derived::process(double)"<<endl;}};intmain(){Derived d;d.process(10);// 调用Derived::process(double)(int转double)// d.process(10, 20); // 如果基类有其他重载,也都被隐藏return0;}

陷阱2:隐藏与重载的混淆

classBase{public:voidf(int){cout<<"Base::f(int)"<<endl;}};classDerived:publicBase{public:voidf(double){cout<<"Derived::f(double)"<<endl;}// 隐藏,不是重载};intmain(){Derived d;d.f(10);// 调用Derived::f(double)!10被转为double// 想要调用Base::f(int)?只能用Base::f(10)return0;}

最佳实践建议

  1. 使用override关键字(C++11)明确表示重写意图
classDerived:publicBase{voidfunc()override;// 明确表示要重写};
  1. 使用using声明引入基类同名函数,避免意外隐藏
classDerived:publicBase{usingBase::func;// 引入基类所有funcvoidfunc();// 现在是重载集的一部分};
  1. 避免在派生类中使用与基类相同的名字,除非你有明确意图

  2. 理解并善用作用域解析Base::member来访问被隐藏的成员

总结

要点说明
隐藏的本质派生类的名字屏蔽了基类的同名成员
判断依据只看名字,不看参数列表
影响范围隐藏一个函数会隐藏所有同名函数
与重写的区别隐藏是静态绑定,重写是动态绑定
解决方案using声明或显式作用域
常见用途加强封装、修改行为、CRTP模式

理解隐藏机制对于正确使用C++继承至关重要,特别是在设计类层次结构和处理多态时。

总结

这篇文章是作者搜集大量面经和资料这里出来的。感谢你的支持
作者wkm是一名中国矿业大学(北京) 大一的新生,希望得到你的关注
如果可以的话,记得一键三联!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 3:16:31

3分钟掌握的Markdown转PPT效率神器

3分钟掌握的Markdown转PPT效率神器 【免费下载链接】md2pptx Markdown To PowerPoint converter 项目地址: https://gitcode.com/gh_mirrors/md/md2pptx 你是否经历过这样的场景&#xff1a;花2小时写好技术文档&#xff0c;却要花4小时调整PPT格式&#xff1f;调查显示…

作者头像 李华
网站建设 2026/8/30 2:47:06

智能文献处理:开源工具助力研究效率提升

智能文献处理&#xff1a;开源工具助力研究效率提升 【免费下载链接】zotero-gpt GPT Meet Zotero. 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-gpt 在信息爆炸的学术环境中&#xff0c;研究人员每天需要处理数十篇文献&#xff0c;传统管理方式面临效率低下、…

作者头像 李华
网站建设 2026/8/30 3:31:13

5大场景掌握Godot Unpacker:让游戏资源提取效率提升80%

5大场景掌握Godot Unpacker&#xff1a;让游戏资源提取效率提升80% 【免费下载链接】godot-unpacker godot .pck unpacker 项目地址: https://gitcode.com/gh_mirrors/go/godot-unpacker 作为游戏开发者&#xff0c;你是否曾遇到过这些困境&#xff1a;项目备份丢失只能…

作者头像 李华
网站建设 2026/8/30 9:44:35

从零到一:Point-NN无参数网络复现与核心原理解析

1. 环境配置&#xff1a;从零搭建Point-NN的实战避坑指南 大家好&#xff0c;我是老张&#xff0c;在AI和3D视觉领域摸爬滚打了十来年。最近CVPR 2023的Point-NN火得不行&#xff0c;号称“无参数、免训练”&#xff0c;直接颠覆了传统点云分析的玩法。我花了几天时间&#xff…

作者头像 李华
网站建设 2026/8/30 10:38:51

突破Mac音频壁垒:Soundflower重构你的音频工作流

突破Mac音频壁垒&#xff1a;Soundflower重构你的音频工作流 【免费下载链接】Soundflower MacOS system extension that allows applications to pass audio to other applications. 项目地址: https://gitcode.com/gh_mirrors/sou/Soundflower 一、当在线会议遭遇&quo…

作者头像 李华