C++|C++ 派生类函数重载与虚函数继承详解


目录

  • 一、作用域与名字查找
    • 1.作用域的嵌套
    • 2.在编译时进行名字查找
    • 3.名字冲突与继承
    • 4.通过作用域运算符来使用隐藏的成员
  • 二、同名函数隐藏与虚函数覆盖
    • 1.几种必须区分的情况
    • 2.一个更复杂的例子

类的关系图:
C++|C++ 派生类函数重载与虚函数继承详解
文章图片

一、作用域与名字查找 1.作用域的嵌套 派生类的作用域嵌套在基类之内
Bulk_quote bulk; cout<< bulk.isbn();

名字isbn解析过程:
  • 因为我们是通过Bulk_quote的对象调用isbn的,所以首先在Bulk_quote中查找,这一步没有找到名字isbn。
  • 因为 Bulk_quote是Disc_quote 的派生类,所以接下来在Disc_quote中查找,仍然找不到。
  • 因为Disc_quote是Quote的派生类,所以接着查找Quote; 此时找到了名字isbn,所以我们使用的isbn最终被解析为Quote中的isbn。
2.在编译时进行名字查找 【C++|C++ 派生类函数重载与虚函数继承详解】成员名字的查找类型由静态类型决定
//给Disc_quote添加一个成员,返回折扣政策 class Disc_quote : public Quote { public : std::pair

我们只能通过Disc_quote及其派生类对象来使用discount_policy。
Bulk_quote bulk; Bulk_qoute *bulkP = &bulk; //静态类型与动态类型一致 Quote *itemP = &bulk; //静态类型为Quote,动态类型不一定 bulkP->discount_policy(); //正确:bulkP的类型是Bulk_quote* itemP->discount_policy(); //错误:itemP的类型是Quote*

尽管在bulk中确实含有一个名为discount_policy的成员,但是该成员对于itemP却是不可见的。
itemP的类型是Quote的指针,意味着对discount_policy的搜索将从Quote开始。
显然Quote不包含名为discount_policy的成员,所以我们无法通过Quote的对象、引用或指针调用discount_policy。
3.名字冲突与继承 派生类可以重用基类中的名字,由于派生类的作用域嵌套在基类中,所以会隐藏基类的同名变量
派生类成员隐藏同名的基类成员
struct Base{ Base():mem(0){} protected: int mem; }; struct Derived : Base{//struct默认public继承 Derived(int i) : mem(i){}; int get_mem() {return mem; } protected: int mem; };

get_mem返回的是在Derived中的mem
Derived d(42); cout<

4.通过作用域运算符来使用隐藏的成员
struct Derived : public Base{ int get_base_mem() {return Base::mem; } //... }; d.get_base_mem(); //输出0

二、同名函数隐藏与虚函数覆盖 1.几种必须区分的情况
派生类函数形式 与基类同名函数的关系 形参列表 绑定方式
非虚函数 隐藏基类同名函数 可相同可不同 静态绑定
虚函数 覆盖基类虚函数 必须相同 动态绑定
使用基类的引用或指针调用虚函数时,会发生动态绑定
  1. 当派生类有基类的同名虚函数且该函数不是虚函数时,无论两个同名函数的参数是否相同。
    • 由于派生类的作用域嵌套在基类内部,所以都会隐藏同名的基类函数
    • 由于不是虚函数,所以即使两函数参数相同,也不会发生动态绑定
//情况1举例 class A{ public : //基类的print不是虚函数 void print() const {cout<<"class A"<

  1. 当派生类有基类的同名函数且该函数是虚函数时
    • 参数列表相同,实现覆盖基类虚函数,可以发生动态绑定
      //情况2:参数列表相同时,虚函数被覆盖 class A{ public : //基类的print是虚函数 void print() const {cout<<"class A"<

    • 参数列表不相同,相当于派生类定义了一个新函数,隐藏了基类虚函数,基类的虚函数没有被覆盖
      class A{ public : //基类的print是虚函数 void print() const {cout<<"class A"<

2.一个更复杂的例子 例子出自《C++ Primer》P550
//类的定义class Base{ public : virtual int fcn(); }; class D1 : public Base{ public: //隐藏基类的fcn,这个fcn不是虚函数 //D1继承了Base::fcn()虚函数的定义 int fcn(int); //形参列表与Base中的fcn不一致 virtual void f2(); //定义一个新的虚函数,它在Base中不存在 }; class D2 : public D1{ public : int fcn(int); //是一个非虚函数,隐藏了D1::fcn(int) int fcn(); //覆盖了虚函数Base::fcn() void f2(); //覆盖了虚函数f2 };

//调用虚函数的例子 //fcn是Base中的虚函数 //D1直接继承Base的虚函数fcn //D2重载了Base的fcn Base bobj; D1 d1obj; D2 d2obj; Base *bp1 = &bobj, *bp2 = &d1jobj, *bp3 = &d2obj; bp1->fcn(); //虚调用:运行时执行Base::fcn bp2->fcn(); //虚调用:运行时执行Base::fcn bp3->fcn(); //虚调用:运行时执行D2::fcn//f2是D1中的虚函数 //Base中没有定义f2 //D2重载了D1的虚函数f2 D1 *d1p = &d1obj; D2 *d2p = &d2obj; bp2->f2(); //错误:Base对象中没有名为f2的成员 d1p->f2(); //虚调用:执行D1::f2() d2p->f2(); //虚调用:执行D2::f2()

//调用非虚函数的例子 //fcn(int)是D1中的 非虚函数 //Base中没有定义fcn(int) //D2中的fcn(int)隐藏了D1中的fcn(int) Base *p1 = &d2obj; //d2obj是D2类型的对象 D1*p2 = &d2obj; D2*p3 = &d2obj; p1->fcn(42); //错误:Base中没有接受int的fcn p2->fcn(42); //静态绑定:调用D1::fcn(int) p3->fcn(42); //静态绑定:调用D2::fcn(int)

    推荐阅读