• 关于类的继承


    传统的C通过预定义、预编译的函数库(如strlen(),rand())等,提供了代码重用的功能。代码重用很重要,方便,且容易维护。
    然而,这具有局限性,函数一般不提供源代码,这样我们无法根据具体需求修改函数。而C++提供了类库,以源代码形式提供,并且继承这种方式可以更好修改和扩展类,只要搞清楚继承的相关知识,继承派生通常比设计一个新类方便。

    1,基类和派生类

    //基类
    class T
    {
    private:
       string firstname;
       string lastname;
       bool hash;
    public:
       T(const string & fn='none',const string &ln='none', bool ht=fasle); //默认构造函数
        voif func(...);
     }
    T(const string & fn,const string &ln,bool ht): firstname(fn),lastname(ln),hash(ht){}//构造函数
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这里有一个插曲,介绍一下类的初始化列表和构造函数初始化的区别。
    T(const string & fn,const string &ln,bool ht): firstname(fn),lastname(ln),hash(ht){}//构造函数是初始化列表。也可以在函数里面赋值初始化。

    区别是,初始化列表可以提高效率。因为函数体内初始化首先会调用默认构造函数构造类成员,再通过赋值运算符给成员赋值,两步操作;列表初始化直接调用拷贝构造函数,一次就够了。
    另外,如果没有默认构造函数 ,必须使用列表初始化,或者const类型的成员必须使用列表初始化,因为const变量不能修改。

    class D :public T
    {
    private:
      int rating;
    public:
      D(int r=0,const string & fn='none',const string &ln='none', bool ht=fasle)
      D(int r,conast T& t): T(t),rating(r) {} 
      其他函数;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    公有继承,基类的公有成员也会成为派生类的公有成员,基类的私有成员也成为派生类的一部分,但只能通过基类的公有和保护方法访问。

    派生类的初始化需要先调用基类的构造函数,再初始化自己增加的成员。析构时先执行派生类的析构函数,再执行基类的。

    二者之间的特殊关系
    1、派生类对象可以使用基类的方法,只要方法不是私有的。
    2、基类指针或引用可以不在显式类型转换的情况下指向派生类对象,但是只能用于调用基类方法。反过来不行。

    2、多态公有继承
    如果我们希望同一个方法在基类和派生类中行为不同,有两种机制可以实现:
    在派生类中重新定义基类的方法,对象调用。
    使用虚函数,对于指针引用来说;

    如果不使用虚函数,程序将根据指针或引用类型来选择方法;如果使用虚函数,将根据指向对象的类型来选择方法;举个例子:

    T & t1_ref=T t(1,1,1);
    T & t2_ref=D d(2,2,2);
    t1_ref.func();
     t2_ref.func();
    
    • 1
    • 2
    • 3
    • 4

    如果func不是虚函数,那么调用的都是基类T的func方法,如果func是虚函数,那么t2_ref调用的就是派生类D定义的func方法。这就实现了多态。
    注意虚函数需要虚析构函数,原因类似。虚析构函数可以根据指针指向对象类型来调用,而不是指针类型调用。比如t2_ref对派生类进行操作,那么就应该调用派生类的析构函数(再调用基类),而不是调用基类T的构造函数。
    另外,基类中定义一个函数是虚函数 ,那么在其派生类中,他也是虚函数,即使不用virtual声明,不过最好还是声明一下。

    接下来说一下虚函数的工作原理;
    为每个对象添加一个隐藏成员虚函数表(vtbl)的指针(vptr).虚函数表就是一个存储很多虚函数地址的数组,派生类没有重新定义虚函数时,用的还是基类的虚函数。调用虚函数时,先查看存储在对象中vtbl的地址vptr,然后转向这个表,看看是第几个虚函数,获得地址,前往这个地址的虚函数并执行。
    所以多态代价是:对象增大、虚函数表的空间、查找地址的时间花费。

    虚函数这么好,但是构造函数不能是虚函数:原因是,派生类不继承基类的构造函数,而是调用它,所以没有意义,并且,虚函数实现需要对象中的vptr,而对象创建在构造函数完成之后,矛盾了。

    关于protected
    保护和private的区别只在基类派生的类中表现出来,派生类可以直接访问基类的保护成员,而基类的私有成员必须通过基类的方法访问。而总是用方法访问很麻烦,所以保护的作用是让派生类可以访问,但是外部不能直接访问,外部只能直接访问类的公有成员。

    抽象基类ABC
    有时二者继承比较麻烦,比如圆和椭圆,椭圆是基类,圆是派生类,但是圆只需要半径就够了,不需要长半轴短半轴等。所以,可以定义一个抽象基类存放二者共性,比如中心坐标,再派生圆和椭圆,这样,只需要抽象基类的指针数组就可以同时管理圆和椭圆(通过多态)
    抽象基类至少有一个纯虚函数(在圆和椭圆中实现不同),纯虚函数就是虚函数加上=0, 如virtual func()=0,纯虚函数甚至可以不用定义。ABC不能实例化。使用ABC迫使派生类遵循ABC制定的接口规范(纯虚函数)。

  • 相关阅读:
    html静态网站基于游戏网站设计与实现共计10个页面 (仿地下城与勇士游戏网页)
    LeetCode 462. 最小操作次数使数组元素相等 II【贪心,排序或快速选择】中等
    浅谈客户端框架设计|一|二|三
    JS的this关键字详解
    Java基础深化和提高 ---- 函数式编程 Lambda
    支持CAN FD的Kvaser PCIEcan 4xCAN v2编码: 73-30130-01414-5如何应用?
    学生HTML个人网页作业作品 使用HTML+CSS+JavaScript个人介绍博客网站 web前端课程设计 web前端课程设计代码 web课程设计
    IDEA 2019 Springboot 3.1.3 运行异常
    dropout
    内卷时代,扫地机器人何时能成为刚需?
  • 原文地址:https://blog.csdn.net/weixin_53344209/article/details/126484111