从零开始的C++(虚析构与IO流)

一、虚函数表

  1. 什么是虚函表,在C++的类中,一旦成员函数中有虚函数,这个类中就会多一个虚函数表指针,这个指针指向一个虚函数表,表里面记录了这个类中所有的虚函数,当这个类被继承,他的子类也会有一个虚函数表,如果子类的成员函数中有函数签名与父类中的虚函数一样就会用子类中的函数替换它在虚函数表中的位置,这样就达到了覆盖的效果
  2. 当通过类指针或引用调用函数时,会根据对象中实际的虚函数表记录来调用函数,这样就达到了多态的效果。
  3. 不管子类有没有虚函数,只要父类中有虚函数,子类继承后中也会有虚函数表

二、虚析构

当使用 delete 释放一个父类指针时,不管实际指向的对象是子类还是父类都只会调用父类的析构函数(多态肯定会出现的问题)
如果子类的析构函数有需要负责释放的内存,就会造成内存泄露

为了解决这个问题,可以把父类的析构函数设置为虚函数,析构函数进行覆盖是不会比较函数名。
当父类的析构函数为虚函数时,通过父类指针或引用释放子类对象时,会自动调用子类的析构函数,子类的析构函数执行完成后也会调用父类的析构函数。
注意:析构函数可以是虚函数,但构造函数不可以。

三、强制类型转换

注意:C++中为了兼容C(语言),目标类型源类型 依然可以继续使用,但C语言的强制类型转换安全性差,因此建议使用C++中的强制类型转换
注意:C++之父认为如果代码设计的完善,根本不需要用到强制类型转换,而C++的强制类型转换之所以设计的很复杂,是为了让程序员多关注代码本身设计,尽量少使用。

C++中的强制类型转换保证没有很大的安全隐患
static_cast<目标类型>(源类型) 编译器会对源类型和目标类型做兼容性检查,不通过则错
dynamic_cast<目标类型>(源类型) 编译器会对源类型和目标类是否同为指针或引用,并且存在多态型的继承关系

const_cast<目标类型>(源类型) 编译器会对源类型和目标类是否同为指针或引用,除了常属性外其他必须完全相同,否则报错
reinterpret_cast<目标类型>(源类型) 编译器会对源类型和目标类是否同为指针和整数进行检查,也就是说把整数转换成指针或把指针转换为整数

动态编译和静态编译 (动态链接,动态链接=》动态库静态库)
静态编译:指针或引用的目标是确定的,在编译期间就确定所有类型检查、函数调用。
动态编译:指针或引用的目标是不确定的(多态),只有函数调用时候才确定,具体是哪个子类
c++和c语言都支持:变长数组

四、I/O流

I/O流的打开模式:
1、ios::in 以读权限打开文件,不存在则失败,存在不清空
5、ios::out 以写权限打开文件,不存在则创建,存在则清空
2、ios::app 文件指针写到末尾(写追加),打开文件用于追加,不存在则创建,存在不清空
3、ios::binary 以二进制模式进行读写
2、ios::ate 文件指针读写全到末尾,打开时定义到文件末尾
6、ios::trunc 打开文件时清空

fstream/ifstream/ofstream 类用于进行文件操作
构造函数或者成员函数 open 用于打开文件
good成员函数检查流是否可用
eof成员函数用于输入流是否结束
>> 操作符用于从文件中读取数据到变量
<< 操作符用于输入出数据到文件

IO流有一系列格式化控制函数,类似:左对齐、右对齐、宽度、填充、小数点位数

二进制的读写:read/write
read (char_type *__s,streamsize __n)
gcount成员函数可以获取上次流的二进制读操作的字节数。

write(char_type *__s,streamsize __n)
good成员函数可以获取到写操作是否成功

随即读写:
seekp(off_type,ios_base::seekdir)
功能:设置文件的位置指针
off_type:偏移值
正值向右,负值向左
seekdir:基础位置
ios::beg 文件开头
ios::cur 当前位置
ios::end 文件末尾

五、类型信息 typeid

  • 用于获取数据的类型信息,返回type_info类型临时对象
    name成员函数,可以获取类型的名字,内建类型名字使用缩写

  • 支持 == 和 != 用来比较是否是同一种类型

  • 如果用于判断父子类的指针或引用,它不能准确判断出实际的对象类型

  • 但可以判断具有多态继承关系的父子类的指针或引用,它的实际对象类

六、异常处理

抛异常
throw 数据
抛异常对象
抛基本类型
注意:不能抛局部对象的指针或引用
如果异常没有被捕获处理,程序就会异常停止
捕获数据
try{
可能抛出异常的代码
}
catch(类型 变量名){//根据数据类型进行捕获
处理异常,如果无法处理可以继续抛出异常
}
注意:捕获异常的顺序是自上而下的,而不是最精准的匹配,针对子类异常捕获时要放在父类的前面
函数的异常声明:
返回值类型 函数名(参数列表)throw(类型1,类型2…)
注意:如果不写异常申明表示司马类型的异常都可能抛出
注意:throw()表示什么都不抛出
注意:如果写了异常声明表示只抛出某些类型的异常,一旦超出异常的范围,程序会直接停止,无法捕获
设计异常类:

		class Error
		{
			int errno;
			char errmsg[255];
		public:
			Error(int errno = -1,const chat* msg="未知错误")
			{
				this->errno = errno;
				strcpy(errmsg,msg)
			}
			int getError(void)
			{
				return errmsg;
			}
		};

猜你喜欢

转载自blog.csdn.net/weixin_45050225/article/details/99996032