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

📅 发布时间:2026/7/8 17:37:56 👁️ 浏览次数:
一文带你深入了解隐藏的相关知识
一文带你深入了解隐藏的相关知识文章目录一文带你深入了解隐藏的相关知识一、什么是隐藏最简单的例子二、隐藏的三种情况1. 成员变量的隐藏2. 成员函数的隐藏3. 重载函数的隐藏三、解决隐藏的方法方法1使用using声明方法2显式调用基类版本四、隐藏 vs 重写覆盖的区别详细解释代码运行结果五、隐藏的实际应用场景场景1加强访问控制场景2修改函数行为但保留接口场景3静态多态CRTP模式中的隐藏六、常见陷阱和注意事项陷阱1意外隐藏陷阱2隐藏与重载的混淆最佳实践建议总结总结好的我来详细讲解C继承中的**隐藏Hiding**机制。这是一个非常重要但也容易混淆的概念。一、什么是隐藏隐藏是指在派生类中定义了与基类同名的成员函数或变量时基类的同名成员在派生类中变得不可见被隐藏了。最简单的例子#includeiostreamusingnamespacestd;classBase{public:voidfunc(){coutBase::func()endl;}};classDerived:publicBase{public:voidfunc(){// 隐藏了基类的funccoutDerived::func()endl;}};intmain(){Derived d;d.func();// 输出Derived::func()基类的被隐藏// 如果想调用基类的必须指定作用域d.Base::func();// 输出Base::func()return0;}二、隐藏的三种情况1. 成员变量的隐藏classBase{public:intvalue100;intsameName10;};classDerived:publicBase{public:intvalue200;// 隐藏了基类的valueintdifferent20;voidprint(){coutDerived value: valueendl;// 200派生类的coutBase value: Base::valueendl;// 100需要指定coutsameName: sameNameendl;// 10基类的没被隐藏}};intmain(){Derived d;d.print();coutd.valueendl;// 200派生类的coutd.Base::valueendl;// 100基类的return0;}2. 成员函数的隐藏这是最容易混淆的地方特别注意隐藏不看参数只看名字classBase{public:voidfunc(){coutBase::func()endl;}voidfunc(intx){coutBase::func(int) xxendl;}voidfunc(doublex){coutBase::func(double) xxendl;}};classDerived:publicBase{public:voidfunc(){// 隐藏了基类中所有名为func的函数coutDerived::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(){coutBase::show()endl;}voidshow(int){coutBase::show(int)endl;}voidshow(double){coutBase::show(double)endl;}};classDerived:publicBase{public:voidshow(){// 隐藏了基类所有的showcoutDerived::show()endl;}};intmain(){Derived d;d.show();// ✅ 调用Derived的// d.show(10); // ❌ 被隐藏了// d.show(3.14); // ❌ 被隐藏了// 解决方案1使用using声明return0;}三、解决隐藏的方法方法1使用using声明classBase{public:voidfunc(){coutBase::func()endl;}voidfunc(int){coutBase::func(int)endl;}};classDerived:publicBase{public:usingBase::func;// 把基类所有func函数引入派生类作用域voidfunc(){// 现在重载而不是隐藏coutDerived::func()endl;}};intmain(){Derived d;d.func();// Derived::func()d.func(10);// Base::func(int)因为using声明return0;}方法2显式调用基类版本classDerived:publicBase{public:voidfunc(){coutDerived::func()endl;}voidcallBaseFunc(){Base::func();// 显式调用基类版本}voidcallBaseFunc(intx){Base::func(x);// 显式调用基类版本}};四、隐藏 vs 重写覆盖的区别这是初学者最容易混淆的一定要分清楚特性隐藏重写覆盖关键字无virtual函数名相同相同参数可不同必须相同返回值可不同必须相同协变除外绑定方式静态绑定动态绑定多态性无有classBase{public:voidhide(){coutBase::hideendl;}// 会被隐藏virtualvoidoverride(){coutBase::overrideendl;}// 会被重写};classDerived:publicBase{public:voidhide(){coutDerived::hideendl;}// 隐藏voidoverride()override{coutDerived::overrideendl;}// 重写};intmain(){Base*ptrnewDerived();ptr-hide();// 静态绑定Base::hide隐藏不看指针实际类型ptr-override();// 动态绑定Derived::overridedeleteptr;return0;}根据你提供的代码ptr-hide();调用的是基类的hide()函数。虽然ptr指向的是一个Derived对象但由于hide()不是虚函数编译器根据指针的静态类型即Base*来决定调用哪个函数这就是静态绑定。详细解释指针类型ptr被声明为Base*类型所以它的静态类型是Base*。函数类型hide()在基类中没有被声明为virtual因此它是一个非虚函数。绑定机制对于非虚函数编译器在编译时就根据指针的静态类型Base*确定了要调用的函数地址所以它会调用Base::hide()。结果因此输出是Base::hide。而override()是虚函数所以调用它时会根据指针指向的对象的实际类型Derived进行动态绑定输出Derived::override。代码运行结果Base::hide // 来自静态绑定 Derived::override // 来自动态绑定总结隐藏hide同名非虚函数由指针/引用的静态类型决定调用哪个版本。重写override同名虚函数由对象的实际类型决定调用哪个版本。五、隐藏的实际应用场景场景1加强访问控制classBase{public:voidpublicAPI(){coutBase public APIendl;}};classDerived:privateBase{public:voidpublicAPI(){// 隐藏基类的publicAPIcoutDeriveds own APIendl;Base::publicAPI();// 可以在内部使用基类的}};intmain(){Derived d;d.publicAPI();// 调用Derived的版本// d.Base::publicAPI(); // ❌ 不能直接调用基类的private继承return0;}场景2修改函数行为但保留接口classLogger{public:voidlog(conststringmsg){cout[LOG] msgendl;}};classTimedLogger:publicLogger{public:voidlog(conststringmsg){// 隐藏基类的logstring timedMsg[getCurrentTime()] msg;Logger::log(timedMsg);// 调用基类版本完成实际工作}private:stringgetCurrentTime(){return12:00:00;// 简化示例}};场景3静态多态CRTP模式中的隐藏templatetypenameDerivedclassBase{public:voidinterface(){static_castDerived*(this)-implementation();// 调用派生类版本}voidimplementation(){// 可能被隐藏coutBase implementationendl;}};classDerived:publicBaseDerived{public:voidimplementation(){// 隐藏基类的implementationcoutDerived implementationendl;}};六、常见陷阱和注意事项陷阱1意外隐藏classBase{public:virtualvoidprocess(intx){coutBase::process(int)endl;}};classDerived:publicBase{public:// 本意是想重写但参数不同变成了隐藏virtualvoidprocess(doublex){// ❌ 隐藏了基类的process(int)coutDerived::process(double)endl;}};intmain(){Derived d;d.process(10);// 调用Derived::process(double)int转double// d.process(10, 20); // 如果基类有其他重载也都被隐藏return0;}陷阱2隐藏与重载的混淆classBase{public:voidf(int){coutBase::f(int)endl;}};classDerived:publicBase{public:voidf(double){coutDerived::f(double)endl;}// 隐藏不是重载};intmain(){Derived d;d.f(10);// 调用Derived::f(double)10被转为double// 想要调用Base::f(int)只能用Base::f(10)return0;}最佳实践建议使用override关键字C11明确表示重写意图classDerived:publicBase{voidfunc()override;// 明确表示要重写};使用using声明引入基类同名函数避免意外隐藏classDerived:publicBase{usingBase::func;// 引入基类所有funcvoidfunc();// 现在是重载集的一部分};避免在派生类中使用与基类相同的名字除非你有明确意图理解并善用作用域解析Base::member来访问被隐藏的成员总结要点说明隐藏的本质派生类的名字屏蔽了基类的同名成员判断依据只看名字不看参数列表影响范围隐藏一个函数会隐藏所有同名函数与重写的区别隐藏是静态绑定重写是动态绑定解决方案using声明或显式作用域常见用途加强封装、修改行为、CRTP模式理解隐藏机制对于正确使用C继承至关重要特别是在设计类层次结构和处理多态时。总结这篇文章是作者搜集大量面经和资料这里出来的。感谢你的支持作者wkm是一名中国矿业大学(北京) 大一的新生希望得到你的关注如果可以的话记得一键三联