头瓜达农鸡

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、类与类之间的关系:

  • 1、类与类之间可能会存在共性
  • 2、类与类之间必定会有差异
  • 3、为节约开发时间和代码量,我们在设计类时可以把类的共享抽象出来形成一个基础类(基类)
  • 4、使用基类 + 差异生成一个新的类叫派生类

二、继承的语法

  • 1、在设计一个类时如果已有的类中有它需要的成员,它可以从已有的类中继承那些成员,被继承的类叫父类或基类,继承类叫做派生类或子类

    class 类名:继承方式 基类1,继承方式 基类2,... 
    {
    
    }

     

  • 2、子类会完全继承父类中的所有成员(包括那些能访问和不能访问的)

  • 3、在设计类时如何形成继承的关系?
    类的设计
    • a、根据问题找对象
    • b、把对象中我们需要的属性和行为抽象出来
    • c、把抽象出的结果记录在一个class中形成类
    • d、把class类实例化构造对象
    • e、对象调用成员函数解决问题
      继承就是在对对象抽象的结果进行归纳总结,找出他们的共性形成基类,再把基类+差异派生出子类
  • 4、继承的优点:代码复用
    子类在继承父类后可以节约大量的代码、节省开发时间、缩短开发周期
  • 5、继承的缺点:
    a、继承会提高类与类之间的耦合度
    b、会降低代码的安全性和可维护性

三、使用继承要注意的问题

  • 1、继承虽然可以获取父类中的所有成员,但子类可以隐藏父类中的同名成员
  • 2、父子类中的函数不可以形成重载,只有在同一个作用域下的同名不同参、const属性的函数才能形成重载
  • 3、父类中的成员在子类的访问属性
    父类中的 public 在子类中 可以访问
    父类中的 protected 在子类中也可以访问
    父类中的 private 在子类中不可以访问
  • 4、继承的方式
    不同的继承方式会影响父类成员变成子类成员后的访问属性,也就是继承来的成员在类外的访问属性
    public 不变
    protected 公有变保护,其他不变
    private 全部变私有

四、虚函数

  • 1、子类中会隐藏父类中的同名函数,这种关系叫做隐藏
  • 2、在同一作用域下同名函数,这种关系叫做重载
  • 3、如果父类中的同名函数添加 virtual 关键字,这种函数就叫作虚函数,子类会覆盖掉父类中的同名函数
  • 4、虚函数是实现多态的基础

五、继承的构造、析构、拷贝

  • 1、在继承时先调用父类的构造函数,再调用子类的构造函数
  • 2、在继承中先调用子类的析构,在调用父类的析构
  • 3、在调用拷贝构造时会先调用父类的拷贝构造,在调用子类的拷贝构造
  • 4、在C++中继承可以有多个父类,在构造时会按照继承的顺序调用父类的构造函数
  • 5、在析构时子类会先调用自己的析构函数,然后按照继承表的逆序调用析构函数

六、虚函数、覆盖、多态

虚函数:成员函数在定义时添加了 virtual 关键字,这种函数叫虚函数
覆盖:如果在子类中实现与父类中的虚函数具有相同的函数,那么子类中的成员函数会覆盖父类中的成员函数
多态:如果子类中的成员函数对父类中的成员函数进行了覆盖,当一个指向子类的父类指针或引用了子类的父类引用,当使用它调用虚函数,然后根据实际的调用对象调用子类中的覆盖函数,而不是父类中的虚函数,这种语法现象叫多态
多态的意义在于,同一种类发出同一种调用,而产生不同的反映

七、覆盖、重载、隐藏

   满足重写的条件:
  • a、必须是成员函数
  • b、必须是虚函数
  • c、函数签名必须相同
  • d、如果返回值是基本类型则必须相同(否则会产生编译错误)
  • e、如果是类类型则必须是父子类关系的指针或引用(必须能进程自动类型转换)

    满足重载的条件:
  • a、必须在同一作用域下
  • b、函数名相同但是参数列表不同
  • c、const属性相同
  • d、返回值的类型不会影响重载

    满足隐藏的条件:
    在父子类之间名字相同的标识符,只要不构成覆盖,则必定构成隐藏

八、多态的条件

  • 1、多态特性除了父子类之间要构成覆盖,还必须是父类以指针或引用的方式指向子类
  • 2、当指针或引用已经构成多态时,此时调用成员所传的this指针再调用成员函数也可以造成多态
  • 3、在子类的构造函数执行前会先调用父类的构造函数,此时由于子类还没有构造完成,因此只能是调用父类中的虚函数,构造函数在进入函数执行体时,类中看的见的资源已经全部构造完成
  • 4、在子类的析构函数执行完成后会再调用父类的析构函数,如果调用被覆盖的虚函数,由于子类已经析构不能算是完整的子类了,因此只能调用父类中的虚函数

九、纯虚函数和抽象类

  • 1、纯虚函数

    class A 
    { 
    public: 
    virtual void test(void) = 0; 
    virtual void test(void) const = 0; 
    }; 

     


    a、纯虚函数不需要被实现,如果非要实现也不能在类中,必须在类外(虚函数)
    b、纯虚函数如果想调用必须在子类中覆盖,然后以多态的方式调用

  • 2、抽象类
    成员函数中有纯虚函数的叫抽象类,这种类不能创建对象
    如果子类继承了抽象类,则必须把父类中的纯虚函数覆盖了,否则它也变成了抽象类不能被实例化
    因此抽象类只能以指针或引用的方式指向子类来调用自己的纯虚函数

  • 3、纯抽象类
    所有的成员函数都是纯虚函数,这种类叫纯抽象类
    面向对象的四大特性:抽象、封装、继承、多态
    纯抽象类可以是类封装的一个过程,同时抽象类也可以当作一个统一的接口类

  • 4、纯抽象类的应用场景
    回调模式:
    函数a由程序员小妹实现完成,如果在此时她想调用小光实现的函数b。1970年
    函数b由程序员小光实现完成,小光在1980年出生
    命令模式:
    输入一个指示然后执行对应的操作
    生产者与消费者模式
    单例模式(饿汉、懒汉)
    工厂模式:一个类以专门制造其它类的己任,这种编程模式叫做工厂模式
    MVC模式

十、C++中的强制类型转换

  • 1、C语言中的强制类型转换还能继续使用,但是不安全
  • 2、C++的强制类型转换使用很麻烦,其实是C++之父不建议使用强制类型转换,一旦代码中需要使用强制类型转换说明代码设计的不合理,强制类型转换是一种亡羊补牢的做法
    静态类型转换
    static_cast<目标类型> (原标识符)
    • 1、自健类型的强制转换
    • 2、void* 与其它类型指针的转换
    • 3、父子类之间的指针转换
      去常类型转换
      const_cast
      重解释类型转换
      reinterpret_cast
      整数与指针之间的转换
      动态类型转换
      dynamic_cast
      用于构成多态的父子类之间指针类型转换

十一、虚函数表

  • 1、什么是虚函数表,当一个类中有虚函数时,编译器会为这个分配一个表专门记录这些虚函数,在类中会有一个隐藏的指针成员来指向这个表
  • 2、如何证明这张表存在
    有虚函数的类会比没有虚函数的类(相同中的)多4个字节,还会添加补齐和对齐
  • 3、一个类只有一张虚函数表,所有的对象共享一张虚函数表
  • 4、一般对象的前4个字节是指向虚函数表的指针变量

十二、动态类型绑定

  • 1、当使用基类的指针或引用指向子类时,编译器并没有立即生成调用函数的指针,而生成了一段代码,用于检查指针指向的真正的对象是什么类型
  • 2、在代码真正运行时才通过对象的指针找到指向虚函数的成员指针
  • 3、再通过成员指针访问到虚函数表,再从中找到调用的函数地址
  • 4、使用多态会产生额外的一些代码和调用,因此使用多态会降低代码的执行速度

十三、类的信息

  • 1、在C++中,使用typeid可以获取类一些信息,以此来确定指针指向的数据类型
  • 2、typeid可以直接使用来判断是否是同一种类型
    typeid(标识符) == typeid(标识符)
  • 3、typeid(标识符).name() 获取类型名,以字符串的形式呈现
  • 4、如果typeid(指针)只能获取到指针的类型,typeid(*指针)可以获取到指针指向的对象实际的类型信息

十四、虚析构

  • 1、如果通过父类指针或引用指向子类对象,当使用delete释放对象时,此时只能调用父类的析构函数,如果在子类中使用new/malloc申请内存资源,那么将导致内存泄露
  • 2、解决方法就是把父类的析构函数设置为虚函数
  • 3、在设计类时如果析构函数什么都不需要做,编译器会生成一个空的析构函数,但这样会让继承它的子类有安全隐患
  • 4、最好把所有的析构都设置为虚函数

十五、异常处理

  • 1、什么是异常:能预见但无法避免的错误。
  • 2、如何抛出异常
    throw 数据;

    • a、可以抛出基本类型的异常
      throw -1;
      throw "我是异常";

    • b、可以抛出类类型的异常
      throw Student stu;

    • c、不要抛局部对象的指针的异常
      Student stu;
      throw &stu;

  • 3、如何捕获异常
    try{
    // 可以会产生异常的代码
    }
    catch(异常类型1)
    {
    // 异常处理代码1
    }
    catch(异常类型2)
    {
    // 异常处理代码2
    }
    catch(异常类型3)
    {
    // 异常处理代码3
    }

    • a、在捕获异常时不光能获得异常,还能获得抛出的异常数据。
    • b、如果异常抛出了,但没有被捕获程序会结束。
    • c、异常的捕获是自上而下的,不是选择是优,因此子类的异常捕获最好放在父类的前面。
    • d、捕获异常时尽量使用引用的方式,由于在抛出异常如果使用对象的方式来捕获就会调用对象的拷贝构造,这样会在拷贝对象过程序中再次引用引发异常。
  • 4、类类型的异常

    • a、可以为每一种异常定义一个什么都不用做的类,它只是为了区分各种异常。
    • b、在抛出异常的时候可能会调用异常的构造、拷贝构造、赋值构造等,如果在类中有看不到资源,一定要把这三个函数实现。
    • c、为了防止有自定义的异常无法被捕获,因此我们在定义异常类时,最好都继承 标准库的异常类,这样哪怕,不能精准捕获异常,也能不遗漏异常。
    • d、在抛异常时,尽量抛匿名临时对象。
  • 5、编译器生会成一段用来申请"安全区"的代码并保护它,在异常发生后,此时程序的节奏已经被打断,没有任何位置是安全的,除了安全区以外,安全区能保证存储在此位置的异常对象不受破坏。

  • 6、构造和析构函数中的异常
    在构造函数中发生了异常后,会直接跳转到异常处理代码,异常的构造就此中断,对象的构造就不完整了,不完整的对象永远不可能调用析构函数,哪怕用delete显示调用。

    在构造函数的异常可以抛但是不要抛出构造函数(内部处理),一般使用回滚机制。

十六、文件IO

操作文件的类:ifstream、ofstream、fstream
open打开文件
打开文件的方式
in 读 ifstream/fstream
out 写 ofstream、fstream
app 追加方式打开,文件不存在则创建,存在不清空
ate 打开文件时定位在文件末尾
binary 以二进制方式打开文件
trunc 以清空方式打开文件
in|out
文本方式的读写使用 << 、>>,与cout、cin类似。
read二进制方式读文件
write二进制方式写文件
seek调整文件的位置指针

posted on 2018-08-16 20:44  头瓜达农鸡  阅读(292)  评论(0编辑  收藏  举报