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


    类的关系图:

    image-20220302182444654

    1|0一、作用域与名字查找

    1|11.作用域的嵌套

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

    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。

    1|22.在编译时进行名字查找

    成员名字的查找类型由静态类型决定

    //给Disc_quote添加一个成员,返回折扣政策 class Disc_quote : public Quote { public : std::pair<int ,double) discount_policy() const {return {quantity,discount};} };

    我们只能通过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。

    1|33.名字冲突与继承

    派生类可以重用基类中的名字,由于派生类的作用域嵌套在基类中,所以会隐藏基类的同名变量

    派生类成员隐藏同名的基类成员

    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<<d.get_mem()<<endl; //打印42

    1|44.通过作用域运算符来使用隐藏的成员

    struct Derived : public Base{ int get_base_mem() {return Base::mem;} //... }; d.get_base_mem(); //输出0

    2|0二、同名函数隐藏与虚函数覆盖

    2|11.几种必须区分的情况

    派生类函数形式 与基类同名函数的关系 形参列表 绑定方式
    非虚函数 隐藏基类同名函数 可相同可不同 静态绑定
    虚函数 覆盖基类虚函数 必须相同 动态绑定

    使用基类的引用或指针调用虚函数时,会发生动态绑定

    1. 当派生类有基类的同名虚函数且该函数不是虚函数时,无论两个同名函数的参数是否相同。
      • 由于派生类的作用域嵌套在基类内部,所以都会隐藏同名的基类函数
      • 由于不是虚函数,所以即使两函数参数相同,也不会发生动态绑定
    //情况1举例 class A{ public : //基类的print不是虚函数 void print() const {cout<<"class A"<<endl;}; }; class B : public A{ public: //B隐藏了A的同名函数 void print() const {cout<<"class B"<<endl;} }; void Test_Print(const A &a){//传入基类的指针 //由于基类的print不是虚函数,所以不会动态绑定 //.print()的结果取决于a的静态类型 //所以无论传入的是A还是B,都打印class A a.print(); } int main(){ A a; B b; Test_Print(a); //打印class A Test_Print(b); //打印class A;因为传入参数的静态类型是A return 0; }
    1. 当派生类有基类的同名函数且该函数是虚函数时

      • 参数列表相同,实现覆盖基类虚函数,可以发生动态绑定

        //情况2:参数列表相同时,虚函数被覆盖 class A{ public : //基类的print是虚函数 void print() const {cout<<"class A"<<endl;}; }; /* class B和Test_Print都不变 */ int main(){ A a; B b; Test_Print(a); //打印class A Test_Print(b); //打印class B;因为发生了动态绑定 return 0; }
      • 参数列表不相同,相当于派生类定义了一个新函数隐藏了基类虚函数,基类的虚函数没有被覆盖

        class A{ public : //基类的print是虚函数 void print() const {cout<<"class A"<<endl;}; }; class B : public A{ public: //B的print(int i)与基类虚函数同名 //但参数列表不同,定义了一个新函数 //基类的虚函数print()没有被覆盖 void print(int i) const {cout<<"print(int i)"<<endl;} }; void Test_Print(const A &a){//传入基类的指针 a.print(); } int main(){ A a; B b; //打印class A Test_Print(a); //打印class A; //因为派生类没有重载虚函数,继续调用基类虚函数 Test_Print(b); //打印print(int i) //调用派生类新增的函数print(int i) b.print(42); return 0; }

    2|22.一个更复杂的例子

    例子出自《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)

    __EOF__

    本文作者萌之上
    本文链接https://www.cnblogs.com/timothy020/p/15959385.html
    关于博主:评论和私信会在第一时间回复。或者直接私信我。
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
  • 相关阅读:
    架构师的 36 项修炼第05讲:架构核心技术之分布式数据存储
    OSPF高级特性 —— 转发地址不可达情况 + 解决
    非DBA人员从零到一,MySQL InnoDB数据库调优之路(四)-数据备份与迁移
    PAT甲级--1035 Password
    腾讯云服务器linux+CentOS7.9+yum源+nginx搭建网站
    重磅消息:23年浙大MEM提前批面试政策正式出炉:金融工程方向停招
    linux操作系统虚拟机的环境配置
    酷克数据推出AI开发工具箱HashML 加速企业级AI应用落地投产
    ChatGPT:你的数字生活助手
    技术分享 | 接口自动化实战演练及测试考察点
  • 原文地址:https://www.cnblogs.com/timothy020/p/15959385.html