目录
1.异常处理方式
在C++中,如果函数在调用时发生异常,异常通常会被传递给函数的调用者进行处理,而不在发生异常的函数内部处理。如果函数调用者也不能处理异常,则异常会继续向上一层调用者传递,直到异常被处理为止,如果最终异常没有被处理,则C++运行系统就会捕捉异常,终止程序运行。
C++的异常处理机制使得异常的引发和处理不必再同一函数中完成,函数的调用者可以在1适当的位置对函数抛出的异常进行处理,这样底层的函数可以着重解决具体的业务问题,而不必考虑对异常的处理。
C++的异常处理通过throw关键字和try...catch语句结构实现,通常情况下,被调用的函数如果发生异常,就通过throw关键字1抛出异常,而函数上层调用者通过try...catch语句检测,捕获异常并对异常进行处理。
throw 表达式;
throw后面的表达式可以是常量,变量或对象。如果函数调用中出现异常,就可以通过throw将表示异常的表达式抛给它的调用者。
函数调用者通过try...catch语句捕获,处理异常,格式如下:
try
{
...
}
catch(异常类型1)
{
...
}
catch(异常类型2)
{
...
}
catch(异常类型n)
{
...
}
try语句块用于检测可能发生异常的代码,如果这段代码抛出了异常,则catch语句会依次对抛出的异常1进行类型匹配,如果某个catch语句中的异常类型与抛出的异常类型相同,则该catch语句就捕获异常并对异常进行处理。
- 一个try...catch语句只能有try语句块,但可以有多个catch语句块。以便与不同的异常类型匹配,catch语句必须有参数,如果try语句块中的代码抛出了异常,无论抛出的值是什么,只要异常的类型与catch语句的参数类型匹配,异常就会被catch语句捕获,最后一个catch语句参数为...符号,表示可以捕获任意类型的异常。
- 一旦某个catch语句捕获到了异常,后面的catch语句将不再被执行。
- try和catch语句块中的代码必须使用大括号括起来。
- try...catch语句不能单独使用。
- 如果try语句块中的某一行代码抛出了异常,则无论异常是否被处理,抛出异常的语句后面的代码都不再被执行。
#include<iostream>
#include<fstream>
using namespace std;
class AbstractException
{
public:
virtual void printErr()=0;
};
class FileException:public AbstractException
{
public:
virtual void printErr()
{
cout<<"错误,文件不存在"<<endl;
}
};
class DivideException:public AbstractException
{
public:
virtual void printErr()
{
cout<<"错误,除零异常"<<endl;
}
};
void readFile()
{
ifstream ifs("log.txt");
if(!ifs)
{
throw FileException();
}
ifs.close();
}
void divide()
{
int num1=100;
int num2=2;
if(num2==0)
{
throw DivideException();
}
int ret=num1/num2;
cout<<"两个数相除结果:"<<ret<<endl;
}
int main()
{
try
{
readFile();
divide();
}
catch(FileException& fex)
{
fex.printErr();
}
catch(DivideException& dex)
{
dex.printErr();
}
catch(...)
{
cout<<"处理其他异常"<<endl;
}
cout<<"程序运行结束"<<endl;
return 0;
}
2.栈解旋
C++不仅能处理各种不同类型的异常,还可以在异常处理前释放所有局部对象。从进入try语句块开始到异常被抛出之前,在栈上创建的所有对象都会被析构,析构的顺序与构造的顺序相反,这一过程称为栈解旋或栈自旋。
#include<iostream>
using namespace std;
class Shape
{
public:
Shape();
~Shape();
static int count;
};
int Shape::count=0;
Shape::Shape()
{
count++;
if(Shape::count==3)
throw "纸张画不下了!!";
cout<<"Shape构造函数"<<endl;
}
Shape::~Shape()
{
cout<<"Shape析构函数"<<endl;
}
int main()
{
Shape circle;
try
{
int num=2;
cout<<"纸张可画图个数:"<<num<<endl;
Shape rectangle;
Shape triangle;
}
catch(const char* e)
{
cout<<e<<endl;
}
return 0;
}
3.标准异常
C++提供了一组标准异常类,这些类以exception为根基类,程序中抛出的所有标准异常都继承自exception类,exception类定义在exception头文件中,定义如下:
class exception
{
public:
exception() noexcept;
exception(const exception&) noexcept;
exception& operator=(const exception&) noexcept;
virtual ~exception();
virtual const char* what() const noexcept;
};
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void speak();
};
void Animal::speak()
{
cout<<"动物叫声"<<endl;
}
class Cat:public Animal
{
public:
virtual void speak();
};
void Cat::speak()
{
cout<<"小猫喵喵叫"<<endl;
}
int main()
{
Animal animal;
Animal& ref=animal;
ref.speak();
try
{
Cat& cat=dynamic_cast<Cat&>(ref);
cat.speak();
}
catch(bad_cast& ex)
{
cout<<ex.what()<<endl;
}
return 0;
}
在exception类定义中,noexcept关键字表示函数不抛出异常。
4.静态断言
断言是编程中常用的一种调试手段。在C++11之前,C++使用assert()宏进行断言,但是它只能在程序运行时期执行,不运行时无法检测到错误,C++11引用静态断言static_assert,格式如下:
static_assert(常量表达式,提示字符串);
在执行断言时,编译器首先检测“常量表达式的值”,若为真,程序完成编译,为假生成一个错误提示为第二个参数。
#include<iostream>
using namespace std;
template<typename T,typename U>
void func(T& t,U& u)
{
static_assert(sizeof(t)==sizeof(u),
"the parameters must be the same width.");
}
int main()
{
int x=100;
int y=20;
char ch='a';
func(x,y);
func(x,ch);
return 0;
}
error: static assertion failed: the parameters must be the same width.