C++之文件IO、类型信息、异常处理(抛异常、捕获异常)

一、虚析构
当使用delete释放一个父类指针时(父类引用),不管指向的是父类对象还是子类对象,都只会调用父类的析构。

当使用多态特性时,如果子类中有需要析构释放的资源,为了避免内存泄漏就需要把父类的析构函数设置为虚函数。

当父类的析构函数为虚函数时,子类的析构会自动覆盖它而不用比较它们的名字,当通过父类或引用来释放子类对象时会自动调用子类的析构函数。

二、文件流
C++把对文件的操作封装到了类中。
ifstream 输入文件流
ofstream 输出文件流
fstream 输入/输出文件流

1、打开文件
使用类的构造函数或open成员函数打开文件,它们的参数一样。
void open( const char *filename, openmode mode );
mode:
ios::app 追加输出
ios::ate 当已打开时寻找到EOF
ios::binary 以二进制模式打开文件
ios::in 为读取打开文件
ios::out 为写入打开文件,
ios::trunc 覆盖存在的文件

ifstream:默认mode
ios::in 为读取打开文件,文件不存在则打开失败
ofstream: 默认mode
ios::out 为写入打开文件,文件不存在则创建
ios::trunc 清空存在的文件
fstream: 默认mode
ios::in 为读取打开文件
ios::out 为写入打开文件
2、关闭流 close成员函数
与标准C的fclose和系统的close功能一样。

3、格式化输入输出
可以完全按照cout/cin的使用方式来读写格式化文件。
在读写类或结构时候可以重载输入输出运算符(<</>>)来提高效率,重载方法与cout/cin的一样。
还可以使用一些格式标志来设置输入输出流的格式,通过flags(), setf(), 和 unsetf() 三个函数来控制。
详细格式志请查看帮助手册。

4、二进制读写
istream &read( char *buffer, streamsize num );
功能:以二进行格式读取数据
buffer:存储数据的缓冲区
num:想要读取的字节数
注意:返回值并不是成功读取到的字节数,而是流对象的引用,可以通过输入流的成员函数gcount获取成功读取到的字节数。

ostream &write( const char *buffer, streamsize num );
功能:以二进制格式写入数据
buffer:待写入的数据的首地址
num:要写入的字节数
注意:返回值不是成功写入的字节数,通过流的good成员函数判断是写入成功。

5、随机读写
istream &seekg( off_type offset, ios::seekdir origin );
功能:以偏移称加基础位置设置文件位置指针

istream &seekg( pos_type position );
功能:以绝对位置设置文件位置指针

pos_type tellg();
功能:获取输入流的文件位置指针

ostream &seekp( off_type offset, ios::seekdir origin );
ostream &seekp( pos_type position );
pos_type tellp();
功能:同上

origin: 基础位置
ios::beg
ios::cur
ios::end

练习1:使用C++文件流实现覆盖检查的cp命令。

#include <iostream>
#include <fstream>
using namespace std;

int main(int argc,const char* argv[])
{
	if(3 != argc)
	{
		cout << "User:cp src dest"<< endl;
		return 0;
	}

	ifstream src(argv[1]);
	if(!src.good())
	{
		cout << "源文件不存在,请检查!" << endl;
		return 0;
	}

	ifstream test(argv[2]);
	if(test.good())
	{
		cout << "目标文件已经存在,是否覆盖(y/n)?";
		char cmd;
		cin >> cmd;
		if('n' == cmd || 'N' == cmd)
		{
			src.close();
			test.close();
			return 0;
		}
	}

	ofstream dest(argv[2]);
	
	char buf[1];

	while(src.read(buf,sizeof(buf)).gcount() > 0)
	{
		dest.write(buf,src.gcount());
	}

	src.close();
	dest.close();
}

三、类型信息
typeid 用于获取数据的类型信息,返回type_info类型临时对象。

使用前要加头文件typeinfo,type_info对象成员函数name可以获取到基本的缩写,自定义类型的名字及名字的长度,以P开头的是指针类型。

type_info对象还支持== !=运行符,能分辨出两种个数据是否是同一种类型。

如果用于判断父子类的指针或引用,它不能准备分辨出实现的对象类型,但如果父类有虚函数,就可以分辨出来。

四、异常处理
在C++中当代码出现问题时,不能通过返回值和errno反映的,而是随机可以返回一个未知的数据来表示错误。

抛异常:
throw 数据(基本类型|自定义类)。
注意:不能抛出局部对象的指针或引用,因为当异常抛出后函数就执行结束了,函数的栈内存会被释放,如果返回局部对象的指针或引用则返回的是野指针或悬空引用。
最好抛临时对象,而上层代码用const引用捕获。
异常捕获:
注意:如果抛出的异常没有被捕获,程序就会停止。
try{
可以抛出异常的代码
}
catch(const 类型1& 变量名)
{
处理异常,如果无法处理可以向上层代码继续抛出异常。
}
catch(const 类型2& 变量名)
{

}

异常捕获是从上到下匹配,只要合适就算匹配成功,并不是择优匹配。
如果异常类型可能是父子类,那么在捕获时要先子类后父类。

函数的异常声明:
在声明函数时可以函数的末尾声明此函数可能抛出的异常。
void func(void) throw(类型1,类型2,…);

1、如果不写异常声明则表示该函数什么类型的异常都可能抛出。
2、如果抛出声明以外的异常类型,则该异常无法捕获,程序肯定会死亡,因此声明异常时要慎重。
3、throw() 表示什么异常都不会抛出

五、C++标准库异常
C++标准库异常定义了常见的异常,可以直接使用,同时C++标准库中也使用这套异常。
这些异常类都有一个what成员函数,里面记录了异常产生的原因。

class Error
{
    int errno;
    char* msg;
    Error(const Error& that) {}
    void operator=(const Error& that) {}
public:
    Error(const char* str="未知类型错误!",int errno=-1):errno(errno)
    {
        msg = new char[strlen(str)+1];
        stdrcpy(msg,str);
    }
     ~Error(void)
    {
        delete[] msg;
    }
    const char* msg(void)
    {
        return msg;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值