c++笔记下

day6

    • 多态

定义

多态

调用一个函数,函数名相同,当传入参数不同时,就会执行不同的函数体

静态(静态库)

在编译阶段把静态库加载到可执行文件中

动态(动态库)

在运行阶段把动态库加载到可执行文件中

绑定

函数调用与函数体绑定

静态绑定

调用一个函数,在编译阶段就知道应该执行哪个函数体

动态绑定

调用一个函数,在运行阶段才知道应该执行哪个函数体

静态多态和动态多态的区别

链接的阶段不同

出现问题

当多个子函数继承同一个父函数的时候,都有同名的函数成员,但是参数都是各自本身的类类型的指针,函数名相同,参数不同,与返回值无关,就会出现函数重载,造成代码冗余。

解决方法

用父类指针保存子类对象的地址或引用保存子类对象本身

出现问题

当用父类指针保存子类对象的地址或引用保存子类对象本身的时候,父类指针或者引用只能去访问子类从父类继承到的成员函数,子类自己的成员函数用父类指针或者引用是访问不到的。

解决方法

方法一:强制类型转换(破坏了类封装的完整性,不安全)

方法二:动态多态(将对应父类的成员函数写成虚函数,然后在子类中重写这个成员函数)。

父类:

virtual 数据类型 函数名()

{

......

}

子类:

//重写,也是虚函数,virtual可写可不写

数据类型 函数名(父类名* 子类对象)/(父类名&子类对象)

{

......

}

动态多态的实现条件

1、有继承关系,一个父类同时拥有多个子类。

2、父类中有虚函数,子类去重写父类的这个虚函数。

3、子类重写父类的这个虚函数的参数是父类的指针或者引用子类的对象。

4、调用这个虚函数的时候就会触发动态多态。

动态多态的实现原理

虚函数表的定义

本质上是一个函数指针数组,存放类中虚函数的地址

  1. 一个类中如果存在虚函数的时候,就会自动生成一个虚函数表。

  1. 虚函数表中存放类中虚函数的地址。

  1. 创建子类对象的时候,如果类中存在虚函数,就会:

先开辟4个字节或8个字节的空间去保存虚函数表在内存中的起始地址。

然后给父类中的数据成员分配内存空间。

最后给子类中的数据成员分配内存空间。

  1. 如果父类中有虚函数,父类中就会有虚函数表,子类是继承父类的,子类中同样会有一个虚函数表。子类和父类用的不是同一个虚函数表。

  1. 如果子类不重写父类的虚函数,虚函数表中保存的就是父类虚函数的地址,如果子类重写父类的虚函数,虚函数表中保存的就是子类虚函数的地址,将父类虚函数的地址覆盖。

动态多态存在的问题

如果用父类指针保存子类对象的地址,释放的时候就得释放(delete)父类数据类型指针,这样只会去执行父类的析构函数,没有执行子类的析构函数,如果在创建子类对象的时候执行的子类构造函数,在子类的构造函数中存在用new开辟的指针空间,没有通过子类的析构函数去释放,就会造成内存泄露。

解决动态多态存在的问题

将父类的析构函数写成虚函数。

原理:如果将父类的析构函数写成虚函数,在释放(delete)父类数据类型的指针时,就会去执行子类的析构函数,从而调用父类的析构函数。

    • 抽象类

纯虚函数应用

在编写父类成员函数的过程中,不知道成员函数的功能该怎样去实现,就会把这个成员函数编写成纯虚函数,不需要写函数体。

纯虚函数的格式

virtual 函数类型 函数名()=0;

抽象类的定义

如果一个类中存在纯虚函数,这个类就变成抽象类。

抽象类的特点

抽象类不能去创建抽象类类型的对象。

抽象类继承的子类仍然可以为抽象类。

抽象类的作用

用来被子类继承,在子类中去重写这个成员函数,实现多态。

    • 抽象类的应用——接口类

接口类的定义

一个类中的所有成员函数都是纯虚函数,只有成员函数,没有数据成员。

接口类的作用

用来被子类继承,在子类中去重写这个成员函数,实现多态。

主要用来描述事物具有的某种能力或者达成某种协议。

day7

    • 类对象内存大小的计算,空类占的内存大小

类对象内存大小的计算

sizeof(类名)

空类占的内存大小

1个字节

空类中默认的六个成员函数

  1. 构造函数

  1. 析构函数

  1. 拷贝构造函数

  1. 取地址(&)运算符重载

  1. const修饰的取地址(&)运算符重载

  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)(宏在使用时中需要加上括号保护起来)

特点

文本直接替换,不会进行数据类型检查

内敛函数

条件

  1. 函数体简短(3~5行)。

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语言中解决方案

  1. 通过调试定位分析原因。

  1. 通过if语句判断返回值。

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:

类名()

{

......

}

......

单例的

两种模式

懒汉模式

等用的时候再去创建这个唯一的对象。

饿汉模式

直接将唯一的对象创建好,等用到它的时候再去用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追求~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值