隐藏规则

在没有虚函数的情况下,如果派生类中声明了与基类成员函数同名的新函数,即使函数的参数表不同,从基类继承的同名函数的所有重载形式也都会被隐藏。如果要访问被隐藏的成员,就需要使用作用域分辨符基类名来限定。

在没有虚函数的情况下,如果某派生类的多个基类拥有同名的成员,同时,派生类又新增这样的同名成员,在这种情况下,派生类成员将隐藏所有基类的同名成员。

#include <iostream>

using namespace std;

class B1
{
public:
    int nV;
    void fun()
    {
        cout<<"Member of B1"<<endl;
    }
};
class B2
{
public:
    int nV;
    void fun()
    {
        cout<<"Member of B2"<<endl;
    }
};
class D1:public B1,public B2
{
public:
    int nV;
    void fun()
    {
        cout<<"Member of D1"<<endl;
    }
};
int main()
{
    D1 d1;
    d1.nV=1;
    d1.fun();

    d1.B1::nV=2;
    d1.B1::fun();

    d1.B2::nV=3;
    d1.B2::fun();
    return 0;
}

在主函数中,创建了一个派生类的对象d1,根据隐藏规则,如果通过成员名称来访问该类的成员,就只能访问到派生类新增加的两个成员,从基类继承过来的成员由于处于外层作用域而被隐藏。这时,要想访问从基类继承来的成员,就必须使用类名和作用域分辨符。程序中后面两组语句就是分别访问由基类B1、B2继承来的成员,程序的运行结果为:

如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些直接基类中,从上一级基类继承来的成员就拥有相同的名称,因此派生类中也就会产生同名现象,对这种类型的同名成员也要使用作用域分辨符来惟一标识,而且必须用直接基类来进行限定。

#include <iostream>

using namespace std;

class B0
{
public:
    int nV;
    void fun()
    {
        cout<<"Member of B0"<<endl;
    }
};
class B1:public B0
{
public:
    int nV1;
};
class B2:public B0
{
public:
    int nV2;
};
class D1:public B1,public B2
{
public:
    int nVd;
    void fun()
    {
        cout<<"Member of D1"<<endl;
    }
};
int main()
{
    D1 d1;
    d1.B1::nV=2;
    d1.B1::fun();

    d1.B2::nV=3;
    d1.B2::fun();
    return 0;
}


有一个基类B0,声明了数据成员nV和函数fun,由B0公有派生产生了类B1和B2,再以B1、B2作为基类共同公有派生产生了新类D1,在派生类中我们不再添加新的同名成员(如果有同名成员,同样遵循隐藏规则),这时的D1类,就含有通过B1、B2继承来的基类B0中的同名成员nV和fun。

间接基类B0的成员经过两次派生之后,通过不同的派生路径以相同的名字出现于派生类D1中,这时如果使用基类B0来限定,同样无法表明成员到底是从B1还是B2继承过来,因此必须使用直接基类B1或者B2的名称来限定,才能够惟一标识和访问成员。

<think>在C++中,当派生继承基时,如果派生中定义了与基同名的成员(包括成员变量和成员函数),那么基的同名成员会被隐藏(hidden)。这就是所谓的“同名隐藏规则。需要注意的是,即使基和派生的成员函数参数列表不同(即函数签名不同),只要函数名相同,基的成员函数也会被隐藏。这与函数重载的规则不同,函数重载要求在同一作用域内。###同名隐藏规则详解:1.**成员变量隐藏**:如果派生定义了与基同名的成员变量,则基的成员变量在派生中被隐藏。在派生中访问该名称时,默认访问的是派生的成员。如果需要访问基的成员,需要使用作用域解析运算符(`::`)。2.**成员函数隐藏**:如果派生定义了与基同名的成员函数(无论参数列表是否相同),基的成员函数在派生中都会被隐藏。也就是说,在派生中通过函数名调用时,只能访问到派生的成员函数,基的所有同名函数都被隐藏。如果需要调用基的成员函数,必须显式使用基名和作用域解析运算符。###示例代码:```cpp#include<iostream>usingnamespacestd;classBase{public:intdata=10;voiddisplay(){cout<<"Basedisplay:"<<data<<endl;}voidshow(intx){cout<<"Baseshow:"<<x<<endl;}};classDerived:publicBase{public:intdata=20;//隐藏的data成员voiddisplay(){//隐藏的display函数cout<<"Deriveddisplay:"<<data<<endl;}//注意:这里没有定义show函数,但基的show函数被隐藏了吗?//实际上,基的show函数仍然被隐藏了,因为派生中定义了一个同名函数(即使参数不同)voidshow(){//隐藏的所有show函数(无论参数如何)cout<<"Derivedshow:"<<data<<endl;}};intmain(){Derivedd;d.display();//调用Derived::display,输出:Deriveddisplay:20d.show();//调用Derived::show,输出:Derivedshow:20//访问基的成员需要使用作用域解析运算符d.Base::display();//调用Base::display,输出:Basedisplay:10d.Base::show(100);//调用Base::show,输出:Baseshow:100//访问基的成员变量cout<<"Basedata:"<<d.Base::data<<endl;//输出:Basedata:10return0;}```###解释:-在派生`Derived`中,我们定义了与基`Base`同名的成员变量`data`和成员函数`display`、`show`。-因此,基的这些同名成员在派生中被隐藏。-在`main`函数中,通过派生对象直接调用`display`和`show`时,调用的是派生的版本。-如果需要调用基的版本,必须使用`Base::`显式指定。###注意:-同名隐藏与函数重载的区别:函数重载发生在同一作用域内,而派生中的函数会隐藏中所有同名函数,即使参数列表不同。因此,在派生中无法直接通过函数名调用基中不同参数的同名函数(除非使用作用域解析运算符)。-如果希望在派生中重载基的函数(而不是隐藏),可以在派生中使用`using`声明引入基的函数,这样基的同名函数就会在派生的作用域内可见,从而形成重载。###使用`using`声明避免函数隐藏:```cppclassDerived:publicBase{public:intdata=20;usingBase::show;//引入基的show函数,使得基的show在派生中可见voiddisplay(){cout<<"Deriveddisplay:"<<data<<endl;}voidshow(){cout<<"Derivedshow:"<<data<<endl;}};intmain(){Derivedd;d.show(100);//现在可以调用基的show(int),输出:Baseshow:100d.show();//调用Derived::show()return0;}```通过`usingBase::show;`,基中名为`show`的所有函数都被引入到派生的作用域中,这样在派生中就可以根据参数列表进行重载解析。###总结:-同名隐藏规则是C++继承中的一个重要规则,它确保了派生中的成员不会与基成员产生歧义,但同时也要求程序员在需要访问基成员时显式指定。-为了避免基函数被意外隐藏,可以使用`using`声明将基函数引入派生作用域。---§§相关问题§§:1.什么是C++中的同名隐藏规则?2.如何访问被隐藏的基成员?3.同名隐藏规则与函数重载有何区别?4.如何使用`using`声明来避免基函数被隐藏?</think>### 同名隐藏规则(Name Hiding)的解释 在C++继承中,**同名隐藏规则**是指当派生定义了与基同名的成员(成员函数或成员变量)时,基的同名成员会被隐藏,无法在派生中直接访问。这与函数重载不同,即使参数列表不同也会发生隐藏。 #### 关键规则: 1. **成员函数隐藏**: - 如果派生定义了与基同名(即使参数不同)的函数,基的所有同名函数都会被隐藏 - 不是函数重载(重载要求在同一作用域) 2. **成员变量隐藏**: - 如果派生定义了与基同名的成员变量,基的成员变量会被隐藏 3. **访问规则**: - 隐藏后必须使用作用域解析运算符`::`显式访问基成员 ### 示例代码说明 ```cpp #include <iostream> using namespace std; class Base { public: void show() { cout << "Base show()" << endl; } void show(int x) { cout << "Base show(int): " << x << endl; } int value = 10; }; class Derived : public Base { public: // 隐藏了Base的所有show()函数(包括不同参数的版本) void show() { cout << "Derived show()" << endl; } // 隐藏了Base的value成员 double value = 3.14; }; int main() { Derived d; // 访问派生成员 d.show(); // 输出: Derived show() cout << d.value; // 输出: 3.14 // 访问被隐藏的基成员 d.Base::show(); // 输出: Base show() d.Base::show(42); // 输出: Base show(int): 42 cout << d.Base::value; // 输出: 10 // 错误示例:无法访问被隐藏成员 // d.show(42); // 编译错误:no matching function // int v = d.value; // 总是访问double版本 return 0; } ``` ### 问题分析 1. **函数隐藏**: - `Derived::show()`隐藏了`Base::show()`和`Base::show(int)` - 即使参数不同,基函数也被完全隐藏 - 不是函数重载(因为不同作用域) 2. **变量隐藏**: - `Derived::value`隐藏了`Base::value` - 访问`d.value`总是得到派生版本 - 需用`d.Base::value`访问基版本 3. **解决方法**: ```cpp class Derived : public Base { public: // 使用using声明引入基函数,形成重载 using Base::show; void show() { cout << "Derived show()" << endl; Base::show(); // 显式调用基版本 } // 使用using引入基变量 using Base::value; }; ``` 现在可以正常调用: ```cpp d.show(42); // 正确:调用Base::show(int) int v = d.value; // 正确:访问Base::value ``` ### 为什么需要同名隐藏规则? 1. **避免意外覆盖**: - 防止派生无意中覆盖基功能 - 要求开发者显式处理同名冲突 2. **作用域隔离**: - 符合C++的作用域规则(内层作用域覆盖外层) - 派生作用域优先于基作用域 3. **型安全**: - 防止不同型成员间的意外转换 - 如示例中`int value`和`double value`不会自动转换 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值