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

博客 动态
0 179
羽尘
羽尘 2022-03-03 13:56:00
悬赏:0 积分 收藏

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

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

类的关系图:

image-20220302182444654

一、作用域与名字查找

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.在编译时进行名字查找

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

//给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。

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

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"<<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.一个更复杂的例子

例子出自《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的fcnBase bobj;D1 d1obj;D2 d2obj;Base *bp1 = &bobj, *bp2 = &d1jobj, *bp3 = &d2obj;bp1->fcn();		//虚调用:运行时执行Base::fcnbp2->fcn();		//虚调用:运行时执行Base::fcnbp3->fcn();		//虚调用:运行时执行D2::fcn//f2是D1中的虚函数//Base中没有定义f2//D2重载了D1的虚函数f2D1 *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的fcnp2->fcn(42);	//静态绑定:调用D1::fcn(int)p3->fcn(42);	//静态绑定:调用D2::fcn(int)
posted @ 2022-03-03 13:32 咪啪魔女 阅读(6) 评论(0) 编辑 收藏 举报
回帖
    羽尘

    羽尘 (王者 段位)

    2335 积分 (2)粉丝 (11)源码

     

    温馨提示

    亦奇源码

    最新会员