标准C++语言-虚函数表

一、虚函数表

  • 在C++的类中,一旦成员函数中有虚函数,此类中就会多一个虚函数表指针,指向一个虚函数表,记录的是类中所有的虚函数
  • 当这个类被继承,它的子类中也会有一个虚函数表
    • 如果子类的成员函数中有函数签名与父类的虚函数一样,就会用子类中的函数替换它的虚函数的位置,这样就达到了覆盖的效果
    • 当通过类指针或引用调用函数时,会根据对象中实际的虚函数表记录来调用函数,这样就达到了多态的效果

二、虚析构

  • 当使用delete释放一个父类指针时,不管实际指向的对象时子类还是父类,都只会调用父类的析构函数多态肯定会出现的问题
  • 如果子类的析构函数有需要负责释放的内存,就会造成内存泄漏
  • 为了解决这个问题,可以把父类的析构函数设置为虚函数(析构函数进行覆盖时,不会比较函数名
    • 当父类的析构为虚函数时,通过父类指针或引用释放子类对象时,会自动调用子类的析构函数,子类的析构函数执行完成后也会调用父类的析构函数
  • 注意:析构函数可以使用虚函数,但构造函数不可以是虚函数

三、强制类型转换

  • C++中为了兼容C语言,(目标类型)源类型依然可以继续使用,但C语言的强制类型转换安全性差,因此建议使用C++中的强制类型转换
  • C++之父认为如果代码设计的完善,根本不需要用到强制类型转换,而C++的强制类型转换之所以设计的很复杂,是为了让程序员多关注代码本身的设计,尽量少使用
  • C++中的强制类型转换可以保证基本没有安全隐患
  • static_cast<目标类型>(源类型):编译器会对源类型和目标类型进行兼容性检查,如果冲突则报错
  • dynamic_cast<目标类型>(源类型):编译器会对源类型和目标类型检查是否同为指针或引用,并且是否存在多态型的继承关系
  • const_cast<目标类型>(源类型):编译器会对源类型和目标类型检测是否同为指针或引用除了常属性外其他部分必须完全相同否则转换失败
  • reinterpret_cast<目标类型>(源类型):编译器会对源类型和目标类型检测是否为指针或整数,可以把指针转换成整数,也可以把整数转换成指针

四、静态编译

  • 指针或引用的目标是确定的,在编译时期就经过了所有的类型检查以及函数调用

五、动态编译

  • 指针或引用的目标是不确定的(多态),只有函数调用时才确定具体是哪个子类

六、I/O流

ios::in:以读权限打开文件,不存在则失败,存在不清空
ios::out:以写权限打开文件,不存在则创建,存在则清空
ios::app:打开文件,用于追加,不存在则创建,存在不清空,在文件头读取,在文件末尾写入
ios::binary:以二进制模式进行读写
ios::ate:打开时定位到文件末尾
ios::trunc:打开文件时清空
fstream/ifstream/ofstream
  • 用于进行文件操作
  • open 用于打开文件(成员函数或构造函数)
  • good 检查流是否可用(成员函数
  • eof 用于输入流是否结束(成员函数
  • << 用于输入数据到文件(操作符
  • >> 用于从文件中读取数据到变量(操作符
IO流有一系列格式化控制函数,类似:左对齐、右对齐、宽度、填充、小数点位数 …

格式化控制函数表
格式标志位1
格式标志位2

二进制读写:
  • 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:文件末尾
获取文件位置指针:tellp()(无参数)
  • 该成员函数返回当前文件流的位置指针(字节数
  • 也可以借助此函数获取文件大小

练习:使用C++标准IO,实现带覆盖检测的cp命令(./cp src dest)

七、类型信息 typeid

  • 用于获取数据的类型信息,返回 type_info 类型临时对象
    • 成员函数name,可以获取类型的名字,内建类型名字使用缩写
    • 同时还支持 == / != 用来比较是否是同一种类型
  • 如果用于判断父子类的指针或引用,它不能准确判断出实际的对象类型
  • 可以判断具有多态继承关系的父子类的指针或引用,实际类型的对象

七、异常处理

抛异常:
  • throw 数据
抛异常对象:
  • 可以抛的是:基本类型
  • 不能抛的是:局部对象的指针或引用
  • 如果异常没有被捕获并处理,程序就会停止
捕获异常:
try{
	可能抛出异常的代码
}
处理异常:
catch(类型 变量名)// 根据数据类型捕获
{
	处理异常,如果无法处理,可以继续抛出异常
}
//捕获异常的顺序是自上而下的,而不是最精准的匹配,针对子类异常捕获时要放在父类的前面
函数的异常声明:
  • 返回值类型 函数名(参数)throw(类型1,类型2,...)
  • 注意:如果不写异常声明表示什么类型的异常都可能抛出
  • 注意:如果写了异常声明表示只抛出某些类型的异常,一旦超出异常声明的范围,程序会直接运行,无法捕获
  • 注意:函数的异常声明throw()表示什么类型都会抛出
设计异常类:
class Error
{
	int errno;
	char errmsg[255];
public:
	Error(int errno = -1,const char* msg="未知错误")
	{
		this->errno = errno;
		strcpy(errmsg,msg);
	}
	int getError(void)
	{
		return errno;
	}
	const char* getErrmsg(void)
	{
		return errmsg;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值