Day46、异常、I/O流
来源:互联网 发布:网络公司财务 编辑:程序博客网 时间:2024/06/07 14:23
一、 异常
1、 常见错误
1) 语法错误
2) 逻辑错误
3) 功能错误
4) 设计缺陷
5) 需求不符
6) 环境异常
7) 操作不当
2、 传统的错误处理机制
1) 通过返回值表示错误
优点:安全,函数调用路径中所有局部对象都能被正确的析构,不会出现内存泄露
缺点:错误处理流程比较复杂,逐层判断,代码臃肿
1.cpp
#include<iostream>
#include<cstdio>
using namespace std;
class A{
public:
A(void){
cout<<"A::A()"<<endl;
}
~A(void){
cout<<"A::~A()"<<endl;
}
};
int func3(void){
Aa;
FILE* fp=fopen("none.txt","r");
if(fp==NULL){
cout<<"file open error!"<<endl;
return -1;
}
//...操作
fclose(fp);
return 0;
}
int func2(void){
Aa;
if(-1==func3()){
return -1;
}
//...其他操作
return 0;
}
int func1(void){
Aa;
if(-1==func2()){
return -1;
}
//...其它操作
}
int main(void){
if(-1==func1()){
return -1;
}
//....
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
A::A()
A::A()
A::A()
file open error!
A::~A()
A::~A()
A::~A()
2) 通过远眺机制处理错误
优点:不需要逐层判断,一步到位错误处理,代码精炼
缺点:函数调用路径中的局部对象失去被析构的机会,会形成内存泄露
2.cpp
#include<iostream>
#include<cstdio>
#include<csetjmp>
using namespace std;
jmp_buf g_env;
class A{
public:
A(void){
cout<<"A::A()"<<endl;
}
~A(void){
cout<<"A::~A()"<<endl;
}
};
int func3(void){
Aa;
FILE* fp=fopen("none.txt","r");
if(fp==NULL){
longjmp(g_env,-1);
}
//...操作
fclose(fp);
return 0;
}
int func2(void){
Aa;
func3();
//...其他操作
return 0;
}
int func1(void){
Aa;
func2();
//...其它操作
}
int main(void){
if(setjmp(g_env)==-1){
cout<<"file open error!"<<endl;
return -1;
}
func1();
//....
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
A::A()
A::A()
A::A()
file open error!
3、 C++异常机制
结合两种错误处理的优点,同时避免它们的缺点,在形式上实现一步到位,同时保证所有局部对象得到正确的析构
4、 C++异常机制
4、1 异常抛出
1)throw异常对象
2)可以抛出基本类型的异常对象
throw -1;
throw “file error!”
3) 可以抛出类类型的对象
class FileError{};
// FileError ex;
// throw ex;
以上两行等同于下一行代码
throw FileError();
4、2 异常捕获
try{
可能发生异常的语句;
}
catch(异常类型1){
针对异常类型1的处理
}
catch(异常类型2){
针对异常类型2的处理
}
……
catch(….) {
针对其他异常的处理
}
举例:
#include<iostream>
#include<cstdio>
#include<csetjmp>
using namespace std;
class A{
public:
A(void){
cout<<"A::A()"<<endl;
}
~A(void){
cout<<"A::~A()"<<endl;
}
};
int func3(void){
Aa;
FILE* fp=fopen("none.txt","r");
if(fp==NULL){
throw -1;//抛出异常 (存在安全区,堆区和栈区之间)
}
//...操作
fclose(fp);
return 0;
}
int func2(void){
Aa;
func3();
//...其他操作
return 0;
}
int func1(void){
Aa;
func2();
//...其它操作
}
int main(void){
try{
func1();
//....(不被执行)
}
catch(int ex){
cout<<"file open error!"<<endl;
return -1;
}
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
A::A()
A::A()
A::A()
A::~A()
A::~A()
A::~A()
file open error!
另一种方式:
#include<iostream>
#include<cstdio>
#include<csetjmp>
using namespace std;
class FileError{
public:
FileError(void){}
FileError(const string& file,int line):
m_file(file),m_line(line){
cout<<"出错位置:"<<m_file<<","<<m_line<<endl;
}
private:
string m_file;
int m_line;
};
class A{
public:
A(void){
cout<<"A::A()"<<endl;
}
~A(void){
cout<<"A::~A()"<<endl;
}
};
int func3(void){
Aa;
FILE* fp=fopen("none.txt","r");
if(fp==NULL){
throw FileError(__FILE__,__LINE__);//预定义的宏
throw FileError();
throw -1;//抛出异常 (存在安全区,堆区和栈区之间)
}
//...操作
fclose(fp);
return 0;
}
int func2(void){
Aa;
func3();
//...其他操作
return 0;
}
int func1(void){
Aa;
func2();
//...其它操作
}
int main(void){
try{
func1();
//....(不被执行)
}
catch(int ex){
cout<<"file open error!"<<endl;
return -1;
}
catch(FileError ex){
cout<<"fileError!"<<endl;
return -1;
}
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
A::A()
A::A()
A::A()
出错位置:3.cpp,29
A::~A()
A::~A()
A::~A()
fileError!
4、3 catch字句根据异常对象的类型自上而下顺序匹配,而不是最优匹配,因此对子类的异常捕获不要放在对基类类型的异常捕获的后面,否则子类异常将被基类的catch子句提前捕获
1#include<iostream>
2using namespace std;
3class A{};
4class B:public A{};
5void func(void){
6 throw B();
7 //throw A();
8 }
9int main(void){
10 try{
11 func();
12 }
13//应该把子类的捕获写在前面,基类写在后面
14//由于向上造型的原因,基类可以匹配子类,而子类不会匹配基类
15 catch(B& ex){
16 cout<<"捕获到异常B"<<endl;
17 return -1;
18 }
19 catch(A& ex){//加引用防止拷贝构造,提高效率
20 cout<<"捕获到异常A"<<endl;
21 return -1;
22 }
23}
tarena@tarena-virtual-machine:~/day47$./a.out
捕获到异常A
4、3 标准异常类
class exeception{
public:
exeception()throw() {}
virtual~exeception() throw() {}
virtualconst char* what() const throw();
};
class A: public exeception{
public:
constchar* what() const throw(){
cout<<”A::error”<<endl;
}
};
class B: public exeception{
public:
constchar* what() const throw(){
cout<<”B::error”<<endl;
}
};
int main(void){
try{
//可能发生A、B …异常
throwA();
}
catch(exeception&ex){
ex.what(); //利用多态的虚函数就可以知道错在哪
}
}
标准库提供的异常:
1#include<iostream>
2using namespace std;
3class FileError:public exception{
4public:
5 const char* what(void)constthrow(){
6 return "file openerror!";
7 }
8};
9int main(void){
10 try{
11 char*p=new char[0xffffffff];//肯定new失败
12 }
13 catch(exception& ex){
14 cout<<ex.what()<<endl;
15 return -1;
16 }
17 return 0;
18 }
tarena@tarena-virtual-machine:~/day47$./a.out
std::bad_alloc
1#include<iostream>
2using namespace std;
3//继承标准异常,覆盖what函数,较少catch语句分支
4class FileError:public exception{
5public:
6 const char* what(void)constthrow(){
7 return "file openerror!";
8 }
9};
10int main(void){
11 try{
12 throw FileError();
13 char*p=new char[0xffffffff];//肯定new失败
14 }
15 catch(exception& ex){
16 cout<<ex.what()<<endl;
17 return -1;
18 }
19 return 0;
20 }
tarena@tarena-virtual-machine:~/day47$./a.out
file open error!
5、 异常说明
1) 可以在函数原型中增加异常说明,说明该函数所可能抛出的异常类型
2) 函数的异常说明是一种承诺,表示该函数所抛出的异常不会超出所说明的范围,如果抛出了异常说明以外的异常对象,该异常无法被捕获,而是继续向上抛出,最终被系统捕获,终止进程
返回类型 函数名(形参表) throw(异常类型表)
void func(string file,int size) throw(FileError,MemoryError){….}
int main(void){
try{
func(file,size);
}
catch(FileError){….}
catch(MemoryError){…..}
catch(int&ex){} //无法捕获
}
3) 两种极端形式
---->不写异常说明,表示可以抛出任何异常
----->空异常说明,throw():表示不会抛出任何异常
#include<iostream>
using namespace std;
class FileError{};
class MemoryError{};
voidfoo(void)/*throw(FileError,MemoryError)*/{
throw MemoryError();
/*throw FileError();
throw -1; */
}
int main(void){
try{
foo();
}
catch(FileError& ex){
cout<<"File error!"<<endl;
return -1;
}
catch(MemoryError& ex){
cout<<"MemoryError!"<<endl;
return -1;
}
catch(...){
cout<<"other error!"<<endl;
return -1;
}
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
MemoryError!
4)如果函数的声明和定义分开书写,要保证异常说明一致,而异常说明类型和顺序无所谓。
#include<iostream>
using namespace std;
class FileError{};
class MemoryError{};
void foo(void)throw(FileError,MemoryError);
int main(void){
try{
foo();
}
catch(FileError& ex){
cout<<"File error!"<<endl;
return -1;
}
catch(MemoryError& ex){
cout<<"MemoryError!"<<endl;
return -1;
}
catch(...){
cout<<"other error!"<<endl;
return -1;
}
return 0;
}
void foo(void)throw(FileError,MemoryError){
throw MemoryError();
/*throw FileError();
throw -1; */
}
6、 构造函数中的异常
1)构造抛出异常,对象将会不完整构造,这样的对象的析构函数永远不会被调用。
2)因此在构造函数抛出异常之前,需要手动销毁在异常产生之前动态分配的资源。
#include<iostream>
#include<cstdio>
using namespace std;
class A{
public:
A(void){cout<<"A::A()"<<endl;}
~A(void){cout<<"A::~A()"<<endl;}
};
class B{
public:
B(void):m_a(newA){
FILE* fp=fopen("none.text","r");
if(fp==NULL){
delete m_a; //出现异常执行这个delete
throw -1;
}
//...操作
fclose(fp);
}
~B(void){ //不出现异常的话执行这个delete
delete m_a;
}
private:
A* m_a;
};
int main(void){
try{
B b;
}
catch(int& ex){
cout<<"错误信息:"<<ex<<endl;
return -1;
}
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
A::A()
A::~A()
错误信息:-1
7、 析构函数中的异常(了解)
//析构函数最好不要抛出异常
#include<iostream>
using namespace std;
class A{
public:
void func(void){
throw -1;
}
~A(void){
throw -2;
}
};
int main(){
Aa;
try{
a.func();
}
catch(int& ex){
cout<<"异常:"<<ex<<endl;
return -1;
}
return 0;
}
tarena@tarena-virtual-machine:~/day47$./a.out
异常:-1
二、 I/O流
1、主要的I/O流类
ios
/ \
istream ostream
/ | \ / | \
istrstream ifstream iostream of…..
2、格式化I/O
1)格式化函数
1 #include<iostream>
2 #include<cmath>
3 using namespace std;
4 int main(void){
5 cout<<sqrt(20000)<<endl;//默认保留6位有效数字
6 cout.precision(10);//取10位有效数字
7 cout<<sqrt(20000)<<endl;
8 cout.setf(ios::scientific);//科学计数法
9 cout<<sqrt(20000)<<endl;
10 cout<<'[';
11 cout.width(10);//10个字符域宽
12 cout.fill('#');//空白位置用“#”填充
13 cout.setf(ios::showpos);//显示正负号
14 cout.setf(ios::internal);//内插对齐(符号靠左,数据靠右)
15 cout<<12345;
16 cout<<']'<<endl;
17 return 0;
18 }
tarena@tarena-virtual-machine:~/day47$./a.out
141.421
141.4213562
1.4142135624e+02
[+####12345]
2)流控制符
1#include<iostream>
2#include<cmath>
3#include<iomanip>
4using namespace std;
5int main(void){
6 cout<<sqrt(20000)<<endl;//默认保留6位有效数字
7 cout<<setprecision(10)<<//精度改为10
8 sqrt(20000)<<endl;
9 cout<<scientific<<sqrt(20000)<<endl;//科学计数法
10 cout<<'[';
11 cout<<setw(10)<<setfill('#')<<showpos<<internal<<12345;
12 cout<<']'<<endl;
13 cout<<'[';
14 cout<<setw(12)<<hex<<left<<showbase<<100;
15 cout<<']'<<endl;
16 return 0;
17 }
tarena@tarena-virtual-machine:~/day47$./a.out
141.421
141.4213562
1.4142135624e+02
[+####12345]
[0x64########]
3)字符串流
#include<strstream>
istrstream、ostrstream、strstream
#include<sstream> (新的)
istringstream、ostringstream、stringstream
输入:
1 #include<iostream>
2 #include<cstdio>
3 #include<sstream>
4 using namespace std;
5 int main(){
6 int i=1234;
7 doubled=3.14;
8 char c='T';
9 char s[]="hello world!";
10 /* char buf[1024]={};//数据缓存区
11 sprintf(buf,"%d %g %c %s",i,d,c,s);//把数据放到内存缓存区buf
12 cout<<buf<<endl; */
13 ostringstream oss;//输出流
14 oss<<i<<' '<<d<<' '<<c<<' '<<s;//把数据插入到Oss所指向的内存缓存区
15 cout<<oss.str()<<endl;//转换成string格式
16 return 0;
17 }
tarena@tarena-virtual-machine:~/day47$./a.out
1234 3.14 T helloworld!
读取:
1 #include<iostream>
2 #include<sstream>
3 using namespace std;
4 int main(){
5 int a;
6 double d;
7 char x;
8 char y[100];
9 istringstream iss;
10 iss.str("100 2.34 A hello world");
11 iss>>a>>d>>x>>y;//输入流
12 cout<<a<<','<<d<<','<<x<<','<<y<<endl;
13 //和sscanf作用一样,准备一个数据缓存区,用sscanf从数据缓存区里读数据
14 return 0;
15 }
100,2.34,A,hello
4)文件流
#include<fstream>
ifstream、ofstream、fstream
向文件输出内容:
1 #include<iostream>
2 #include<fstream>
3 using namespace std;
4 int main(){
5 //向文件输出:fprintf
6 ofstream ofs("file.txt");
7 ofs<<1234<<' '<<56.78<<' '<<"helloworld"<<endl;
8 ofs.close();
9 }
tarena@tarena-virtual-machine:~/day47$./a.out
tarena@tarena-virtual-machine:~/day47$cat file.txt
1234 56.78 hello world
从文件读取内容:
1 #include<iostream>
2 #include<fstream>
3 using namespace std;
4 int main(){
5 //向文件输出:fprintf
6 /*ofstream ofs("file.txt");
7 ofs<<1234<<' '<<56.78<<' '<<"helloworld"<<endl;
8 ofs.close(); */
9
10 //从文件输入:fscanf
11 int i;
12 double d;
13 strings;
14 ifstream ifs("file.txt");//已存在
15 //从文件中提取一个整型数放入i中,提取浮点数放入d中,提取字符串放入s中
16 ifs>>i>>d>>s;//跟file文件的内容一一对应
17 cout<<i<<','<<d<<','<<s<<endl;
18 ifs.close();
19 return 0;
20 }
1234,56.78,hello
5)随机读写
istream& istream::
seekg(off_type offset, ios::seekdir origin)
ostream& ostream::
seekg(off_type offset, ios::seekdir origin)
offset:偏移位置,整数向后偏移,负数向前偏移
origin:取值:
ios::beg –从文件头偏移
ios::cur –从当前位置偏移
ios::end –从文件尾偏移
1 #include<iostream>
2 #include<fstream>
3 using namespace std;
4 int main(){
5 fstream fs("file.txt",ios::in|ios::out);//指定读写权限
6 fs<<"0123456789";//向文件写入数据
7 fs.seekp(-3,ios::cur);
8 fs<<"XYZ";//file的文件内容:0123456XYZ
9
10 fs.seekg(3,ios::beg);
11 string str;
12 fs>>str;
13 cout<<str<<endl;// 3456XYZ
14
15 fs.seekp(ios::beg);
16 fs<<"ABC";//file的文件内容ABC3456XYZ
17 return 0;
18 }
6)二进制I/O(fread / fwrite 、 read / write)
二进制写:ostream& ostream::write(constchar * buffer , size_t num)
二进制读:istream& istream::read(char * buffer , streamsize num)
二进制写举例:
1 #include<iostream>
2 #include<fstream>
3 using namespace std;
4 int main(void){
5 ofstream ofs("123.txt");
6 char wbuf[]="tangzihaoo@126.com";
7 ofs.write(wbuf,sizeof(wbuf));
8 ofs.close();
9 return 0;
10 }
tarena@tarena-virtual-machine:~/day47$cat 123.txt
tangzihaoo@126.comtarena
二进制读举例:
1 #include<iostream>
2 #include<fstream>
3 using namespace std;
4 int main(void){
5 ofstream ofs("123.txt");
6 char wbuf[]="tangzihaoo@126.com";
7 ofs.write(wbuf,sizeof(wbuf)); //向文件写入数据
8 ofs.close();
9
10 ifstream ifs("123.txt");
11 char rbuf[100]={0};
12 ifs.read(rbuf,sizeof(rbuf));//从文件读数据到rbuf里
13 cout<<rbuf<<endl;//输出buf到屏幕 tangzihaoo@126.com
14 return 0;
15 }
- Day46、异常、I/O流
- I/O流与异常
- I/O流与异常
- day_11_异常、I/O流
- 异常处理和I/O流
- 异常,文件,I/O流知识小结
- 异常+文件+I/O流+设计模式
- I/O与异常
- I/O异常
- Python I/O 和 异常
- 第四章 Java的I/O流和异常处理
- Java I/O异常的处理
- Java异常处理和I/O处理
- I/O 流
- java I/O流
- c++I/O流
- I/O流
- java I/O流
- web配置p6spy
- #342 – 将Button 和Command绑定(Binding a Button to a Command)
- ftp开发机命令使用
- 小白初次使用云主机云服务器(腾讯云)
- SpringMVC跨域乱码问题
- Day46、异常、I/O流
- 警告背后的use after free
- SpringMvc 遇到的坑,返回中文乱码以及Ajax跨域
- Java实现图片的压缩(包括文件上传的图片压缩)
- CentOS升级Python 2.6到2.7
- 归档与解归档
- 最大最小距离算法(K-MEANS K-medoids )聚类算法的结合运用
- Android Demo分享
- zookeeper原理分析大纲