异常无处不在,程序随时可能误入歧途!C++ 提出了新的异常处理机制!
*异常*是一种程序控制机制,与函数机制互补
函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它可以在出现“意外”时中断当前函数,并以某种机制(类型匹配)回馈给隔代的调用者相关的信息.
通过函数返回值来处理错误。
#include
#include
#define BUFSIZE 1024
//实现文件的二进制拷贝
int copyfile(char *dest, char *src){
FILE *fp1 = NULL, *fp2 = NULL;
//rb 只读方式打开一个二进制文件,只允许读取数据
fopen_s(&fp1, src, "rb");
if(fp1 == NULL){
return -1;
}
//wb 以只写的方式打开或新建一个二进制文件,只允许写数据。
fopen_s(&fp2, dest, "wb");
if(fp2 == NULL){
return -2;
}
char buffer[BUFSIZE];
int readlen, writelen;
//如果读到数据,则大于0
while( (readlen = fread(buffer, 1, BUFSIZE, fp1)) > 0 ){
writelen = fwrite(buffer, 1, readlen, fp2);
if(readlen != writelen){
return -3 ;
}
}
fclose(fp1);
fclose(fp2);
return 0;
}
void main(){
int ret = 0;
ret = copyfile("c:/test/dest.txt", "c:/test/src.txt");
if(ret != 0){
switch(ret){
case -1:
printf("打开源文件失败!\n");
break;
case -2:
printf("打开目标文件失败!\n");
break;
case -3:
printf("拷贝文件时失败!\n");
break;
default:
printf("出现未知的情况!\n");
break;
}
}
system("pause");
}
c++异常处理机制
// demo 15-15
#include
#include
#include
using namespace std;
#define BUFSIZE 1024
//实现文件的二进制拷贝
int copyfile2(char *dest, char *src){
FILE *fp1 = NULL, *fp2 = NULL;
//rb 只读方式打开一个二进制文件,只允许读取数据
fopen_s(&fp1, src, "rb");
if(fp1 == NULL){
throw new string("文件不存在");
}
//wb 以只写的方式打开或新建一个二进制文件,只允许写数据。
fopen_s(&fp2, dest, "wb");
if(fp2 == NULL){
throw -2;
}
char buffer[BUFSIZE];
int readlen, writelen;
//如果读到数据,则大于0
while( (readlen = fread(buffer, 1, BUFSIZE, fp1)) > 0 ){
writelen = fwrite(buffer, 1, readlen, fp2);
if(readlen != writelen){
throw -3 ;
}
}
fclose(fp1);
fclose(fp2);
return 0;
}
int copyfile1(char *dest, char *src){
return copyfile2(dest, src);
}
void main(){
int ret = 0;
try{
ret = copyfile1("c:/test/dest.txt", "c:/test/src.txt");
}catch(int error){
printf("出现异常啦!%d\n", error);
}catch(string *error){
printf("捕捉到字符串异常:%s\n", error->c_str());
delete error;
}
system("pause");
}
异常发生第一现场,抛出异常
void function( ){
//... ...
throw 表达式;
//... ...
}
在需要关注异常的地方,捕捉异常
try{
//程序
function();
//程序
}catch(异常类型声明){
//... 异常处理代码 ...
}catch(异常类型 形参){
//... 异常处理代码 ...
}catch(...){ //其它异常类型
//
}
注意事项:
通过throw操作创建一个异常对象并抛掷
在需要捕捉异常的地方,将可能抛出异常的程序段嵌在try块之中
按正常的程序顺序执行到达try语句,然后执行try块{}内的保护段
如果在保护段执行期间没有引起异常,那么跟在try块后的catch子句就不执行,程序从try块后跟随的最后一个catch子句后面的语句继续执行下去
catch子句按其在try块后出现的顺序被检查,匹配的catch子句将捕获并按catch子句中的代码处理异常(或继续抛掷异常)
如果没有找到匹配,则缺省功能是调用abort终止程序。
提示:处理不了的异常,我们可以在catch的最后一个分支,使用throw语法,继续向调用者throw。
可以在函数声明中列出可能抛出的所有异常类型,加强程序的可读性。
如:
int copyfile2(char *dest, char *src) throw (float, string *, int)
1.对于异常接口的声明,在函数声明中列出可能抛出的所有异常类型
2.如果没有包含异常接口声明,此函数可以抛出任何类型的异常
3.如果函数声明中有列出可能抛出的所有异常类型,那么抛出其它类型的异常讲可能导致程序终止
4.如果一个函数不想抛出任何异常,可以使用 throw () 声明
// demo 15-17
#include
#include
#include
using namespace std;
#define BUFSIZE 1024
//实现文件的二进制拷贝
//第一种情况,throw 普通类型,和函数返回传值是一样的
int copyfile2(char *dest, char *src){
FILE *fp1 = NULL, *fp2 = NULL;
//rb 只读方式打开一个二进制文件,只允许读取数据
fopen_s(&fp1, src, "rb");
if(fp1 == NULL){
//int ret = -1;
char ret = 'a';
throw ret;
}
//wb 以只写的方式打开或新建一个二进制文件,只允许写数据。
fopen_s(&fp2, dest, "wb");
if(fp2 == NULL){
throw -2;
}
char buffer[BUFSIZE];
int readlen, writelen;
//如果读到数据,则大于0
while( (readlen = fread(buffer, 1, BUFSIZE, fp1)) > 0 ){
writelen = fwrite(buffer, 1, readlen, fp2);
if(readlen != writelen){
throw -3 ;
}
}
fclose(fp1);
fclose(fp2);
return 0;
}
int copyfile1(char *dest, char *src){
return copyfile2(dest, src);
}
void main(){
int ret = 0;
try{//保护段
//printf("开始执行 copyfile1...\n");
ret = copyfile1("c:/test/dest.txt", "c:/test/src.txt");
//printf("执行 copyfile1 完毕\n");
}catch(int error){
printf("出现异常啦!%d\n", error);
}catch(char error){
printf("出现异常啦!%c\n", error);
}
system("pause");
}
// demo 15-18
#include
#include
#include
using namespace std;
#define BUFSIZE 1024
//第二种情况,throw 字符串类型,实际抛出的指针,而且,修饰指针的const 也要严格进行类型匹配
int copyfile3(char *dest, char *src){
FILE *fp1 = NULL, *fp2 = NULL;
//rb 只读方式打开一个二进制文件,只允许读取数据
fopen_s(&fp1, src, "rb");
if(fp1 == NULL){
const char * error = "大佬,你的源文件打开有问题";
printf("throw 前,error 的地址:%p\n", error);
throw error;
}
//wb 以只写的方式打开或新建一个二进制文件,只允许写数据。
fopen_s(&fp2, dest, "wb");
if(fp2 == NULL){
throw -2;
}
char buffer[BUFSIZE];
int readlen, writelen;
//如果读到数据,则大于0
while( (readlen = fread(buffer, 1, BUFSIZE, fp1)) > 0 ){
writelen = fwrite(buffer, 1, readlen, fp2);
if(readlen != writelen){
throw -3 ;
}
}
fclose(fp1);
fclose(fp2);
return 0;
}
int copyfile1(char *dest, char *src){
return copyfile3(dest, src);
}
void main(){
int ret = 0;
try{//保护段
//printf("开始执行 copyfile1...\n");
ret = copyfile1("c:/test/dest.txt", "c:/test/src.txt");
//printf("执行 copyfile1 完毕\n");
}catch(int error){
printf("出现异常啦!%d\n", error);
}catch(char error){
printf("出现异常啦!%c\n", error);
}catch(string error){
printf("出现异常啦!%s\n", error.c_str());
}catch(const char *error){
printf("出现异常啦(char *)!%s(地址:%p)\n", error, error);
}catch(...){
printf("没捉到具体的异常类型\n");
}
system("pause");
}
// demo 15-19
#include
#include
#include
using namespace std;
#define BUFSIZE 1024
class ErrorException{
public:
ErrorException(){
id = 0;
printf("ErrorException 构造!\n");
}
~ErrorException(){
printf("ErrorException ~析构!(id: %d)\n", id);
}
ErrorException(const ErrorException &e){
id = 1;
printf("ErrorException 拷贝构造函数!\n");
}
int id;
};
//第三种情况,throw 类类型,最佳的方式是使用引用类型捕捉,抛出匿名对象
//当然,如果是动态分配的对象,直接抛出其指针
//注意:引用和普通的形参传值不能共存
int copyfile4(char *dest, char *src){
FILE *fp1 = NULL, *fp2 = NULL;
//rb 只读方式打开一个二进制文件,只允许读取数据
fopen_s(&fp1, src, "rb");
if(fp1 == NULL){
//ErrorException error1;
throw ErrorException(); //throw ErrorException();
}
//wb 以只写的方式打开或新建一个二进制文件,只允许写数据。
fopen_s(&fp2, dest, "wb");
if(fp2 == NULL){
throw -2;
}
char buffer[BUFSIZE];
int readlen, writelen;
//如果读到数据,则大于0
while( (readlen = fread(buffer, 1, BUFSIZE, fp1)) > 0 ){
writelen = fwrite(buffer, 1, readlen, fp2);
if(readlen != writelen){
throw -3 ;
}
}
fclose(fp1);
fclose(fp2);
return 0;
}
int copyfile1(char *dest, char *src){
return copyfile4(dest, src);
}
void main(){
int ret = 0;
try{//保护段
//printf("开始执行 copyfile1...\n");
ret = copyfile1("c:/test/dest.txt", "c:/test/src.txt");
//printf("执行 copyfile1 完毕\n");
}catch(ErrorException error){
printf("出现异常啦!捕捉到 ErrorException 类型 id: %d\n", error.id);
}catch(ErrorException &error){
//error.id = 2;
printf("出现异常啦!捕捉到 ErrorException &类型 id: %d\n", error.id);
}catch(ErrorException *error){
printf("出现异常啦!捕捉到 ErrorException *类型 id: %d\n", error->id);
delete error;
}catch(...){
printf("没捉到具体的异常类型\n");
}
system("pause");
}
异常也是类,我们可以创建自己的异常类,在异常中可以使用(虚函数,派生,引用传递和数据成员等)
案例:设计一个数组类容器 Vector,重载[]操作,数组初始化时,对数组的个数进行有效检查
1) index<0 抛出异常errNegativeException
2) index = 0 抛出异常 errZeroException
3)index>1000抛出异常errTooBigException
4)index<10 抛出异常errTooSmallException
5)errSizeException类是以上类的父类,实现有参数构造、并定义virtual void printError()输出错误。
// demo 15-20
#include
using namespace std;
/*
设计一个数组类容器 Vector,重载[]操作,数组初始化时,对数组的个数进行有效检查
1)index<0 抛出异常errNegativeException
2)index = 0 抛出异常 errZeroException
3)index>1000抛出异常errTooBigException
4)index<10 抛出异常errTooSmallException
5)errSizeException类是以上类的父类,实现有参数构造、并定义virtual void printError()输出错误。
*/
class errSizeException{
public:
errSizeException(int size){
m_size = size;
}
virtual void printError(){
cout<<"size: "< 1000){
throw errTooBigException(len);
}else if(len < 10){
throw errTooSmallException(len);
}
m_len = len;
m_base = new int[len];
}
Vector::~Vector(){
if(m_base) delete[] m_base;
m_len = 0;
}
int Vector::getLength(){
return m_len;
}
int &Vector::operator[](int index){
return m_base[index];
}
void main(){
try{
Vector v(10000);
for(int i=0; i
C++的异常处理机制使得异常的引发和异常的处理不必在同一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计对不同类型异常的处理。
异常是专门针对抽象编程中的一系列错误进行处理的,C++中不能借助函数机制实现异常,因为栈结构的本质是先进后出,依次访问,无法进行跳跃,但错误处理的特征却是遇到错误信息就想要转到若干级之上进行重新尝试, 如图:
// demo 15-21
#include
#include
#include
using namespace std;
class Student{
public:
Student(int age){
if(age > 249){
throw out_of_range("年龄太大,你是外星人嘛?");
}
m_age = age;
m_space = new int[1024*1024*100];
}
private :
int m_age;
int *m_space;
};
void main(){
try{
for(int i=1; i<1024; i++){
Student * xiao6lang = new Student(18);
}
}catch(out_of_range &e){
cout<<"捕捉到一只异常:"<