C++(继承):16---继承中类的作用域

一、派生类的作用域嵌套在其基类的作用域之内

  • 例如:有下面的代码
class Quote {
public:
    double isbn() {
        return 3.14;
    }
};

class Disc_quote :public Quote {};

class Bulk_quote :public Disc_quote {};

int main()
{
    Bulk_quote bulk;
    cout << bulk.isbn();
    return 0;
}
  • bulk对象在调用isbn()函数时,会:
    • 从Bulk_quote类中查找是否有该函数,发现没有
    • 进一步查找Disc_quote类,发现Disc_quote类中也没有该函数
    • 进一步查找Quote,发现存在,于是执行Quote中的isbn()函数

二、在编译时进行名字查找

  • 一个对象、引用、或指针的静态类型决定了该对象的哪些成员是可见的。即使静态类型与动态类型可能不一致(例如使用基类的指针/引用指向于派生类)
  • 举个例子:
class Quote {};

class Disc_quote :public Quote {
public:
    std::pair<size_t, double> discount_policy()const {}
};
class Bulk_quote :public Disc_quote {};

int main()
{
    Bulk_quote bulk;
    Bulk_quote *bulkP = &bulk;
    Quote *itemP = &bulk;

    bulkP->discount_policy();  //正确,bulkP的类型是Bulk_quote
    itemP->discount_policy();  //错误,itemP的类型是Quote
    return 0;
}
  • 上述代码错误的那一行是因为使用基类的指针指向于派生类,但是通过这个指针调用了属于派生的成员(但是基类中没有定义),该成员对于基类来说是不可见的

三、隐藏

四、虚函数与隐藏

  • 通过隐藏的知识点我们知道,虚函数为什么在基类与派生类中的参数列表必须一致了。如果参数列表不一致,那么基类的虚函数在派生类中没有被重写,而是被隐藏了(就无法通过基类的引用或指针调用派生类的虚函数了)

演示案例

class Base {
public:
    virtual int fcn();  //虚函数
};

class D1 :public Base {
public:
    //因为参数列表不一致,因此Base的虚函数没有被覆盖(重写),而是被隐藏了
    //所以下面的fcn函数时D1的一个普通成员函数,不是虚函数
    int fcn(int);
    virtual void f2(); //虚函数
};

class D2 :public D1 {
public:
    int fcn(int); //非虚函数,隐藏了D1::fcn(int)
    int fcn();    //覆盖了Base的虚函数fcn()
    void f2();    //覆盖了D1的虚函数f2()
};
  • 下面都是对虚函数的调用
int main()
{
    Base bobj;
    D1 d1obj;
    D2 d2obj;
    Base *pb1 = &bobj;
    Base *pb2 = &d1obj;
    Base *pb3 = &d2obj;

    pb1->fcn(); //虚调用,运行时调用Base::fcn()
    pb2->fcn(); //虚调用,运行时调用Base::fcn()
    pb3->fcn(); //虚调用,运行时调用D2::fcn()

    D1 *d1p = &d1obj;
    D2 *d2p = &d2obj;

    d1p->f2(); //虚调用,运行时调用D1::f2()
    d2p->f2(); //虚调用,运行时调用D2::f2()
    return 0;
}
  • 还有以下对于非虚函数fcn(int)的调用
int main()
{
    D2 d2obj;
    Base *p1 = &d2obj;
    D1   *p2 = &d2obj;
    D2   *p3 = &d2obj;

    //错误p1->fcn(42); Base没有用fcn(int)函数
    p2->fcn(42); //静态绑定,调用D1::fcn(int)
    p3->fcn(42); //静态绑定,调用D2::fcn(int)
	return 0;
}

五、覆盖重载的函数

  • 从上面的隐藏知识点来看,对于派生类的函数,不论是成员函数还是虚函数都可以被重载
  • 有时一个基类有很多重载函数,而派生类重写了基类的函数,那么默认情况下,基类中的这些所有的重载函数都不可以使用了,见下面的演示案例:
class A {
public:
    void func();
    void func(int);
    void func(string);
};

class B :public A {
public:
    void func(); //此时A中的三个func都被覆盖了,只能使用无参数的func斑斑
};

int main()
{
    B b;
    b.func();       //正确
    //b.func(10);   错误
    //b.func("HelloWorld"); 错误
    return 0;
}
  • 如果我们希望只覆盖基类中的一部分函数,而其他函数在派生类中还没有被覆盖,一种使用方法就是使用前面文章介绍的using声明。这样的话就无须覆盖基类中的每一个重载版本了
  • using声明只需要给出名称,而不需要给出参数列表,因此基类中的所有重载函数在派生类中都可以使用了
  • 演示案例:
class A {
public:
    void func();
    void func(int);
    void func(string);
};

class B :public A {
public:
    void func();
    //使用using声明,此时只有无参数的func被覆盖了,而另外两种func在派生类中没有被覆盖
    using A::func;
};

int main()
{
    B b;
    b.func();            //正确
    b.func(10);          //正确
    b.func("HelloWorld");//正确
    return 0;
}
发布了1481 篇原创文章 · 获赞 1026 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/104433950
今日推荐