day6
- 多态
定义 | 多态 | 调用一个函数,函数名相同,当传入参数不同时,就会执行不同的函数体 | |||
静态(静态库) | 在编译阶段把静态库加载到可执行文件中 | ||||
动态(动态库) | 在运行阶段把动态库加载到可执行文件中 | ||||
绑定 | 函数调用与函数体绑定 | ||||
静态绑定 | 调用一个函数,在编译阶段就知道应该执行哪个函数体 | ||||
动态绑定 | 调用一个函数,在运行阶段才知道应该执行哪个函数体 | ||||
静态多态和动态多态的区别 | 链接的阶段不同 | ||||
出现问题 | 当多个子函数继承同一个父函数的时候,都有同名的函数成员,但是参数都是各自本身的类类型的指针,函数名相同,参数不同,与返回值无关,就会出现函数重载,造成代码冗余。 | ||||
解决方法 | 用父类指针保存子类对象的地址或引用保存子类对象本身 | ||||
出现问题 | 当用父类指针保存子类对象的地址或引用保存子类对象本身的时候,父类指针或者引用只能去访问子类从父类继承到的成员函数,子类自己的成员函数用父类指针或者引用是访问不到的。 | ||||
解决方法 | 方法一:强制类型转换(破坏了类封装的完整性,不安全) | ||||
方法二:动态多态(将对应父类的成员函数写成虚函数,然后在子类中重写这个成员函数)。 父类: virtual 数据类型 函数名() { ...... } 子类: //重写,也是虚函数,virtual可写可不写 数据类型 函数名(父类名* 子类对象)/(父类名&子类对象) { ...... } | |||||
动态多态的实现条件 | 1、有继承关系,一个父类同时拥有多个子类。 2、父类中有虚函数,子类去重写父类的这个虚函数。 3、子类重写父类的这个虚函数的参数是父类的指针或者引用子类的对象。 4、调用这个虚函数的时候就会触发动态多态。 | ||||
动态多态的实现原理 | 虚函数表的定义 | 本质上是一个函数指针数组,存放类中虚函数的地址 | |||
先开辟4个字节或8个字节的空间去保存虚函数表在内存中的起始地址。 然后给父类中的数据成员分配内存空间。 最后给子类中的数据成员分配内存空间。
| |||||
动态多态存在的问题 | 如果用父类指针保存子类对象的地址,释放的时候就得释放(delete)父类数据类型指针,这样只会去执行父类的析构函数,没有执行子类的析构函数,如果在创建子类对象的时候执行的子类构造函数,在子类的构造函数中存在用new开辟的指针空间,没有通过子类的析构函数去释放,就会造成内存泄露。 | ||||
解决动态多态存在的问题 | 将父类的析构函数写成虚函数。 原理:如果将父类的析构函数写成虚函数,在释放(delete)父类数据类型的指针时,就会去执行子类的析构函数,从而调用父类的析构函数。 |
- 抽象类
纯虚函数应用 | 在编写父类成员函数的过程中,不知道成员函数的功能该怎样去实现,就会把这个成员函数编写成纯虚函数,不需要写函数体。 |
纯虚函数的格式 | virtual 函数类型 函数名()=0; |
抽象类的定义 | 如果一个类中存在纯虚函数,这个类就变成抽象类。 |
抽象类的特点 | 抽象类不能去创建抽象类类型的对象。 抽象类继承的子类仍然可以为抽象类。 |
抽象类的作用 | 用来被子类继承,在子类中去重写这个成员函数,实现多态。 |
- 抽象类的应用——接口类
接口类的定义 | 一个类中的所有成员函数都是纯虚函数,只有成员函数,没有数据成员。 |
接口类的作用 | 用来被子类继承,在子类中去重写这个成员函数,实现多态。 主要用来描述事物具有的某种能力或者达成某种协议。 |
day7
- 类对象内存大小的计算,空类占的内存大小
类对象内存大小的计算 | sizeof(类名) |
空类占的内存大小 | 1个字节 |
空类中默认的六个成员函数 |
|
- explicit转换构造函数
出现的问题 | 类内: 类名(1个参数) { ...... } main函数: 类名类对象名=构造函数中参数对应数据类型的数据; 当进行上方的语句操作时,其他数据类型的数据会自动转换成类类型的数据。 | |
解决问题 | 用explicit修饰构造函数。 类内: explicit 类名(1个参数) { ...... } | |
被explicit修饰的构造函数,用类名类对象名=构造函数中参数对应数据类型的数据;创建对象的时候就会报错,不允许有这种操作。 | ||
构造函数的种类 | 1、默认构造函数:无参或者所有参数都有默认值 2、普通的构造函数:含有2个或者2个以上参数,普通的构造函数的参数可以部分有默认值。 3、拷贝构造函数:拥有1个参数,并且参数是类类型引用。 4、转换构造函数:用explicit 修饰的构造函数有一个参数,不是这个类类型,是其他是数据类型。 |
- final修饰类和类的成员函数
final修饰类 |
格式 | class 类名 final { ...... } |
作用 | 用final修饰类不能被继承 | |
final修饰类的虚函数 | 格式 | class 类名 { virtual 数据类型 函数名()final { ...... } ...... } |
作用 | 用final修饰父类的虚函数时,继承的子类中不能对这个虚函数进行重写 |
- inline修饰类的成员函数——内联函数
回顾宏定义 | 格式 | 常量(定义宏) #define 名字 数值 | |
例1 | #define PI 3.1415 | ||
例2 | define add(x,y) (x+y)(宏在使用时中需要加上括号保护起来) | ||
特点 | 文本直接替换,不会进行数据类型检查 | ||
内敛函数 |
条件 |
2、逻辑简单(没有for、while等语句)。 3、该成员函数频繁被调用。 | |
格式 | 子类:重写父类虚函数 inline 数据类型 函数名() { ...... } | ||
性质 | 用inline修饰类的成员函数,不一定是内敛函数,只是对编译系统做出建议,如果不符合内联函数的条件,该成员函数就不是内联函数。 | ||
特点 | 调用的时候会进行参数类型的检查,看是否符和函数要求 | ||
宏定义与内联函数的关系 | 内联函数是对宏定义的优化,弥补C中宏定义的一些不足 |
- 类型转换函数
回顾 数据 类型 转换 | 强制类型转换(自己转换) | 例:int a = 10;float b;b = (float)a; | |||
隐式类 型转换 |
| 横向箭头:不管我们有没有进行混合运算,都势必进行转换。 | |||
竖向箭头:只有在进行混合运算的时候,才会进行转换。 | |||||
C++中四种 类型转换 | const_cast | 只有一种用途,去掉类型的const或volatile属性 (将不能修改的改成可以修改的) | |||
static_cast(最常用) | 无条件转换,静态类型转换(类型强转) (基本数据类型的转换) | ||||
dynamic_cast | 子类转父类。有条件转换,动态类型转换,运行时检查类型安全(转换失败返回NULL) (多态类之间的转换) | ||||
reinterpret_cast | 仅重新解释类型,但没有进行二进制转换 (不同指针类型的转换) | ||||
const_cast |
格式 | const 类名 对象名1; 类名& 对象名2=const_cast<类名&>(对象名1); 这样就可以对对象1的内容进行修改了。 | |||
static_cast(最常用) |
格式 | int a=10; int *pa=&a; double *d=static_cast<double *>&a; 等同C语言中的强制类型转换。 | |||
dynamic_cast | 条件 | 必须有虚函数 | |||
特点 | 安全的父类类型和子类类型的转换,进行类型检查 | ||||
reinterpret_cast | 作用 | 用来进行函数指针类型的转换 |
- 异常处理
定义 | 在编译过程中对,会出现错误或者异常,就要进行异常处理 | |||
语法错误 | 看报错提示去解决问题 | |||
逻辑错误 | 程序异常结束,例如死循环、野指针、数组越界、逻辑判断没有处理等等 | |||
C语言中解决方案 |
| |||
C++中解决方案 | try(可能发生异常的代码) |
格式 | try { ......(可能出现错误的代码) } | |
throw (抛出异常)
catch (捕获异常) | 格式 | 抛出异常:(int类型) throw -1; 捕获异常: catch(int a) { ...... } | ||
格式 | 抛出异常:(自定义类类型) throw 类名(“返回内容”); 捕获异常: catch(类名 a) { ...... } | |||
格式 | 抛出异常:(string类型) throw string(“返回内容”); 捕获异常: catch(string a) { ...... } | |||
格式 | 抛出异常:(exception这个类是C++中标准异常类——父类) throw exception(“返回内容”); 捕获异常: catch(exception a) { ...... } |
- 智能指针
智能指针的作用 | 当手动开辟的空间生命周期结束时,自动释放空间(自动执行析构函数) | |
三种智能指针 |
共享智能指针 | 多个指针变量同时指向同一块堆区的内存空间,当所有指针的生命周期都结束时,才会去自动释放这片申请到的空间。 |
独享智能指针 | 同一时刻只有一个指针指向一块堆区的内存空间,当这个指针的生命周期都结束时,就会自动释放这片申请到的空间。 | |
弱型共享智能指针 | 多个指针变量同时指向同一块堆区的内存空间,同时有一个弱型的指针也指向这块空间,当弱型的指针生命周期结束时,不会去自动释放这块空间,只有当其他非弱型的指针生命周期都结束时,才会去自动释放这片申请到的空间。 | |
格式 | shared_ptr<类名>p(new 类名); //p为指针变量 等价于 类名*p=new 类名; ...... delete p; |
- 单例模式
定义 | 一个类,只会创建一次对象(如打开数据库的类,整个程序中只会被打开一次) | |||
方法 |
1、构造函数私有化 2、提供一个成员函数去访问这个唯一创建的对象 3、将这个成员函数变成静态的成员函数,因为静态成员函数没有对象时也可以调用
函数名1功能:获取唯一的对象 函数名2功能:保存唯一的对象 | 类内: public: ...... static 类名*函数名1() { ...... } static 类名*函数名2 { ...... } private: 类名() { ...... } ...... | ||
单例的 两种模式 | 懒汉模式 | 等用的时候再去创建这个唯一的对象。 | ||
饿汉模式 | 直接将唯一的对象创建好,等用到它的时候再去用。 |