继承中类的作用域

Posted xiaojianliu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了继承中类的作用域相关的知识,希望对你有一定的参考价值。

派生类的作用域嵌套在其基类的作用域之内,如果一个名字无法在派生类的作用域内无法正确解析,则编译器将继续在外层的基类作用域中寻找该名字的定义。

名字冲突与继承

派生类中能重定义在其直接基类或间接基类中的名字,此时定义在内层作用域(即派生类)的名字将隐藏定义在外层作用域(即基类)的名字。

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

struct Derived : Base{
    Derived(int i):mem(i){}
    int get_mem() {return mem;}
protected int mem;      //@ 隐藏基类中的mem
};

通过作用域符来使用隐藏的成员

可以通过作用域符来访问被隐藏的基类成员:

struct Derived : Base{
    int get_base_mem(){return Base::mem;}
    //...
};

作用运算符将覆盖掉原有的查找规则,并指示编译器从 Base 类的作用域开始查找 mem

注意:

除了覆盖继承而来的虚函数之外,派生类最好不要重用其他定义在基类中的名字。

一如往常,名字查找先于类型检查

如果派生类的成员与基类的某个成员同名,则派生类将在其作用域内隐藏该基类成员,即使派生类成员与基类成员的形参列表不一致,基类成员也会被隐藏。

struct Base{
    int memfunc();
};

struct Derived : Base{
    int memfunc(int);
};

Derived d;Base b;
b.memfunc();        //@ 正确
d.memfunc(10);      //@ 正确
d.memfunc();        //@ 错误,参数列表为空的基类成员函数被隐藏
d.Base::memfunc();      //@ 正确

虚函数与作用域

基类和派生类的虚函数接受的实参必须相同,否则就无法通过基类的引用或者指针调用派生类的虚函数。

class Base{
public:
    virtual int fun();
};

class D1 : public Base{
public:
    int fun(int);       //@ 隐藏了Base中的fun函数
    virtual void f2();
};

class D2 : public D1{
public:
    int fun(int);       //@ 隐藏了D1 中的 fun 函数
    int fun();          //@ 覆盖了Base 中的 fun 函数
    void f2();          //@ 覆盖了 D1 中的 f2 函数
};

通过基类调用隐藏的虚函数

Base bobj;D1 d1obj;D2 d2obj;
Base* bp1 = &bobj,*bp2 = &d1obj,*bp3 = &d2obj;

bp1->fun();     //@ 调用Base::fun
bp2->fun();     //@ 调用Base::fun
bp3->fun();     //@ 调用D2::fun

D1 *d1p = &d1obj;D2 *d2p = &d2obj;
bp2->f2();      //@ 错误,Base没有名字为f2的成员
d1p->f2();      //@ 调用D1::f2
d2p->f2();      //@ 调用D2::f2

Base *p1 = &d2obj;D1 *p2 = &d2onj;D2 *p3 = &2dobj;
p1->fun(42);        //@ 错误,Base中没有接受一个int的fun
p2->fun(42);        //@ 静态绑定,调用D1::fun(int)
p3->fun(42);        //@ 静态绑定,调用D2::fun(int)

覆盖重载的函数

成员函数无论是否是虚函数,都可以被重载,派生类可以覆盖重载函数的0个或多个实例,如果派生类希望所有的重载版本对它来说都是可见的,那么它就需要覆盖所有的版本,或者一个也不覆盖。

有时候只需要覆盖重载集合中的一个版本,而不得不覆盖基类中的每一个版本,显然很麻烦。一种的好的解决办法是为重载成员提供一条 using 声明语句,这样就不需要覆盖基类中的每一个重载版本。using 声明语句指定了一个名字而不指定形参列表,所以一条基类成员函数的 using 声明语句就能把该函数的所有重载实例添加到派生类的作用域中,此时派生类只需要定义其特有的版本即可,无需为继承而来的。

以上是关于继承中类的作用域的主要内容,如果未能解决你的问题,请参考以下文章

Java中类的继承,属性和方法的四种修饰符的作用范围,final关键字,java的三大特点中的2个:封装和多态,以及多态的一个设计模式,模板方法模式(template method)

Python中类的继承及类的属性和方法总结

es6中类中的静态属性实例属性静态方法实例方法的个人理解

java中类继承,到底继承了什么?

Python rest-framework 中类的继承关系(as_view)

java中类的加载情况