C++未知点 作用域与成员函数

C++Primer P499

“在基类和派生类中使用同一名字的成员函数,在派生类作用域中派生类成员将屏蔽基类成员,即使函数原型不同,基类成员也会被屏蔽:”

struct base
{
    int memfcn();
};

struct Derived: base
{
    int memfcn(int);
};
int main()
{
    Derived d;
    base b;

    b.memfcn();
    d.memfcn(10);
    d.memfcn();  //error?编译器需要查找名字memfcn,并在Derived类中找到。一旦找到了名字,编译器就不再继续找了。
    d.base::memfcn();

    return 0;
}
注1: 派生类中定义的函数也不重载基类中定义的成员。通过派生类对象调用函数时,实参必须与派生类中定义的版本相匹配,只有在派生类根本没有定义该函数时,才考虑基类函数

注2:如果派生类重定义了重载成员,则通过派生类只能访问派生类中重定义的那些成员。


“派生类不用重定义所继承的每一个基类版本,它可以为重载成员提供using声明。一个using 声明只能指定一个名字,不能指定形参表。因此,为基类成员函数名称而做的using声明将改函数的所有重载实例加到派生类的作用域。”将所有名字加入作用域后,派生类只需要重定义本类型确实必须定义的那些函数。“

开始有点不理解,看了下林锐的高质量编程上的实例。

P250 成员函数的重载、覆盖与隐藏

成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数类型、顺序或数目不同(包括const和非const参数);
(4)virtual 关键字可有可无。
覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字

P251 令人迷惑的隐藏规则

隐藏“是指派生类的成员函数遮蔽了与其同名的基类成员函数,具有规则:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

下段代码中:

(1)函数Derived::f(float)覆盖了Base::f(float)。
(2)函数Derived::g(int)  隐藏了Base::g(float),而不是重载。
(3)函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。

class Base
{
public:
    virtual void f(float x)
    {
        cout << "Base::f(float) " << x << endl;
    }
    void g(float x)
    {
        cout << "Base::g(float) " << x << endl;
    }
    void h(float x)
    {
        cout << "Base::h(float) " << x << endl;
    }
};

class Derived : public Base
{
public:
    virtual void f(float x)
    {
        cout << "Derived::f(float) " << x << endl;
    }
    void g(int x)
    {
        cout << "Derived::g(int) " << x << endl;
    }
    void h(float x)
    {
        cout << "Derived::h(float) " << x << endl;
    }
};

int  main(void)
{
    Derived  d;
    Base *pb = &d;
    Derived *pd = &d;
    //好:程序的行为仅依赖于对象的真实类型
    pb->f(3.14f);  //Derived::h(float)
    pd->f(3.14f);  //Derived::h(float)

    //不好:程序的行为依赖于指针的静态类型
    pb->g(3.14f);
    pd->g(3.14f); //静态绑定:对象在Derived类中找到g(),就会停止查找了,此时3.14f被转换为int型

   //不好:程序的行为依赖于指针的静态类型
    pb->h(3.14f);  //静态绑定:Base::h(float)
    pd->h(3.14f); //静态绑定:  Derived::h(float)

    return 0;
}
提示:如果确实想使用所谓的”跨越类边界的重载“,可以在派生类定义中的任何地方显示地使用using关键字。

class Derived:public Base
{
    public:
    using Base::g;
    void g(int x){...}
};
P253 摆脱隐藏

class Base
{
public:
   void f(int x);
};

class Derived:public Base
{
    public:
    void f(char *str);
};

/*
如果要让pd->f(10)调用函数Base::f(int )
1. 使用using声明;
2. 把类Derived修改如下
*/

//1. using 声明
class Derived:public Base
{
    public:
   // using Base::f(int x);
    //using  Base::f(int);
    using Base::f;  //using声明只能指定一个名字,不能指定形参表
    void f(char *str);
};

//2.
class Derived:public Base
{
    public:
    void f(char *str);
    void f(int x){Base::f(x);}
}
void Test(void)
{
    Derived *pd=new Derived;

    pd->f(10);  //编译器查找名字f,在Derived中找到,发生类型不匹配错误(数字10不能被隐式转换为字符串)

}
但是,隐藏规则的存在有会带来好处:

(1)写语句pd->f(10)的人可能真的想调用Derived::f(char *)函数,只是他误将参数写错了。有了隐藏规则,编译器就可以明确指出错误,这未必不是好事。否则,编译器会静悄悄地将错就错,程序员将很难发现这个错误,流下祸根。

(2)假如类Derived 有多个基类(多重继承),有时搞不清楚哪些基类定义了函数f。如果没有隐藏规则,那么pd->f(10)可能会调用一个出乎意料的基类函数f。尽管隐藏规则看起来不怎么有道理,但它的确能消灭这些意外。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值