C++ Primer第五版笔记——继承中的类作用域

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rest_in_peace/article/details/81941137

每个类定义自己的作用域,在作用域中定义类的成员,当存在继承关系时,派生类的作用域嵌套在其基类的作用域内。当一个成员名字在派生类的作用域内无法解析的时候,编译器将继续在外层的基类作用域中寻找该名字的定义。
举个例子:类B中有一个函数名为f,类D继承自类B,当通过D的对象调用函数f时会有如下流程:
1.首先在D中查找,这一步没有找到名字f;
2.因为D是B的派生类,所以接下来在B中查找,此时找到名字f,所以使用的f函数最终被解析为B中的f。
因此,当调用p->mem()或obj.mem()时,将依次执行以下步骤:
1.首先确定p(或obj)的静态类型。因为调用的是一个成员,因此该类型必然是类类型;
2.在p(或obj)的静态类型对应的类中查找mem().如果找不到,则依次在直接基类中不断查找直到继承链的顶端,如果仍然找不到,编译器将报错;
3.一旦找到mem,将进行常规类型检查以确定这次的调用是否合法;
4.假如调用合法,则编译器将根据调用的是不是虚函数而产生不同的代码。

名字冲突与继承:
派生类能重用定义在其直接基类或者间接基类中的名字:

struct Base{
    Base():mem(0){}
protected:
    int mem;
};

struct Der:Base{
    Der(int i):mem(i){} 
    int getm(){return mem;}
    //通过作用域运算符使用隐藏的成员
    int get_b_m(){return Base::mem;}    
protected:
    int mem;
};

int main(void){
    Der d(42);                   //Base::mem默认初始化为0
    cout<<d.getm()<<endl;        //输出42
}

名字检查优于类型判断:
即使在派生类成员与基类成员的参数列表不相同,基类成员依然会被隐藏:

class Base {
public:
    int f(){}
};
class Sb:public Base{
public:
    int f(int i){}
};

int main(void) {
    Base b;
    Sb s;
    b.f();               //正确
    s.f(12);             //正确
    s.f();               //报错
}

编译器首先在Sb中查找名字f,因为在Sb中确实有一个名为f的成员,因此查找过程就终止了,而Sb中的f需要一个参数,调用的语句无法提供实参,所以调用语句错误。

虚函数与作用域:
以上也是基类和派生类中的虚函数必须有相同参数列表的原因:

class Base{
public:
    virtual int f();
};
class D1:public Base{
public:
    int f(int);               //形参不一致,这个不是虚函数
    virtual void f2();
};

class D2:public D1{
public:
    int f();                 //覆盖Base的虚函数f
    int f(int);              //不是虚函数,隐藏D1的f(int)
    void f2();               //覆盖D1的f2()
};

覆盖重载的函数:
和其它函数一样,成员函数无论是不是虚函数都能被重载。派生类可以覆盖重载函数的0个或多个实例。如果派生类希望所有的重载版本对它来说都是可见的,那么他就需要覆盖所有版本,或者一个也不覆盖。
有时一个类仅需覆盖重载集合中的一些而非全部函数,此时,如果不得不覆盖基类的每一个版本的话,操作将变得极为繁琐。
一种好的解决方法就是使用using关键字,因为using声明语句指定的是一个名字而不指定参数列表,所以一条基类成员函数的using语句就能把该函数的所有重载实例添加到派生类的作用域中。此时,派生类只需要定义其特有的函数就可以了,而无需为继承而来的其它函数重新定义。

class Base {
public:
    int f(){}
    int f(int i){}
    int f(int i,int j){}
    int f(double d){}
    int f(double d,double e){}
    void f(int a,double v){}
    void f(int e,char a){}
    double f(char s){}
    char f(int r,char s,double a){}
    char f(double g,char w){}
    char f(double d,int f){}

};
class Sb:public Base{
public:
    int f(){}          //在这里只重载了一个函数,
};

class Sb2:public Base{
public:
                    //一个都不重载
};

class Sb3:public Base{
public:
    using Base::f;        //使用using声明
    int f(){}            //只重载部分
};

int main(void) {
    Sb s;
    Sb2 s2;
    Sb3 s3;

    s.f(2);           //报错
    s2.f(2);          //正常
    s3.f(2);          //正确

}

猜你喜欢

转载自blog.csdn.net/rest_in_peace/article/details/81941137
今日推荐