1.异常处理异常:
c++的异常处理机制使得异常的引发和处理不必在同一函数中,这样底层的函数可以着重解决具体问题,而不必过多地考虑对异常的处理。上层调用者可以在适当的位置设计对不同类型异常的统一处理。
语法
如果某段程序发现了自己不能或不想处理的异常,就可以使用throw表达抛出这个异常,将它抛给调用者。throw的操作数表示异常类型,语法上与return语句的操作数类似。如果程序中有多种要抛出的异常,应该用不同的操作数类型来互相区别。
try子句后的复合语句是受保护的代码段。如果预料某段程序代码或对某函数的调用可能发生异常,就将它放在try块中。如果这段代码运行时真的发生异常,其中的throw表达式就会抛出这个异常。
catch子句是异常处理程序,捕获由throw表达式抛出的异常。异常声明部分指明了子句处理异常的类型和异常参数名称。类型可以是任何有效的数据类型,包括C++的类。当异常被抛出以后,catch子句便依次检查,若某个catch子句声明的异常类型与被抛出的异常类型一致,则执行该段代码。如果异常类型是一个省略号,catch子句便处理所有类型的异常,这段处理程序必须是catch子句的最后一个分支。
例子:借用函数模板写的数组
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
template<typename T>
class arraylist{
public:
arraylist(){
len=0;
ARRAY_SIZE=10;
dp=new T[ARRAY_SIZE];
}
void add(T dat){
if(len==ARRAY_SIZE){
ensurecapacity();
}
*(dp+len)=dat;
len++;
}
int get(int index){
if(index<0||index>=len){
printf("index out of bounds exception!\n");
throw index;//将错误的index抛出
}
return *(dp+index);
}
int size(){
return len;
}
void travel(){
printf(":============================\n");
printf("size=%d\n",len);
int i=0;
for(;i<len;i++){
cout<<*(dp+i);
if((i+1)%10==0){
printf("\n");
}
}
printf(":============================\n");
}
~arraylist(){
delete []dp;
}
protected:
int len;
int ARRAY_SIZE;
T* dp;
void ensurecapacity(){
T*tp=dp;
ARRAY_SIZE*=2;
dp=new T[ARRAY_SIZE];
int i=0;
for(;i<len;i++){
*(dp+i)=*(tp+i);
}
delete []tp;
}
};
int main(){
int index;
arraylist<int> list;
for(int i=1;i<=100;i++){
list.add(i);
}
list.travel();
printf("please input index:\n");
cin>>index;
try{//尝试运行
printf("list.get(%d)=%d\n",index,list.get(index));//如果get中的index不对那么不会执行的。
printf("-------test---------\n");
}catch(int e){
printf("%d is out of bounds\n",e);
}
printf("game over!\n");
return 0;
}
例子:除零异常
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw a;
}
return a / b;
}
int main() {
int a, b;
while (1) {
try {
printf("please input two number\n");
cin >> a >> b;
printf("%d/%d=%d\n", a, b, divide(a, b));
} catch (int e) {
printf("%d is divided by zero!\n", e);
}
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
class dividebuzeroexception{
char* msg;
public:
dividebuzeroexception(char* msg){
printf("dividebuzeroexception constructor....\n");
this->msg=msg;
}
dividebuzeroexception(const dividebuzeroexception& e){
printf("dividebuzeroexception copy constrcutor....\n");
this->msg=e.msg;
}
char* what(){
return msg;
}
};
int divide(int a,int b){
if(b==0){
throw dividebuzeroexception((char*)"index out of bounds exception!\n");
//生成一个没有名字的对象,默认为const
}
return a/b;
}
int main(){
while(1){
int a,b;
printf("input two numbers:\n");
cin>>a>>b;
try{
printf("%d/%d=%d\n",a,b,divide(a,b));
}catch(int e){
printf("%d is divide by zero!\n",e);
}catch(dividebuzeroexception& e){
//这里会调用复制构造函数复制上面的无名的const对象
//所以上面的复制构造函数要加const
printf("%s",e.what());
}
}
}
1.异常在divide函数中被抛出,由于在divide函数中没有对异常进行处理,
2.catch处理程序的出现顺序很重要,因为在一个try块中,异常处理程序是按照它出现的次序被检查和匹配的。只要找到一个匹配的异常类型,后面的异常处理都将被忽略。所以catch(…)应该放在最后
有时,在函数内部处理异常,并不合适
2.异常接口声明
(1).为了增强程序的可读性和安全性,使函数的用户能够方便地知道所使用的函数会抛出哪些异常,可以在函数的声明中列出这个函数可能抛出兵所有异常类型,例如:
void fun() throw (A, B, C, D);
(2).如果在函数的声明中没有包括异常接口声明,则此函数可以抛出任何类型的异常,一个不抛出任何类型异常的函数可以这样声明:
void fun() throw ();
如果一个函数抛出了它的异常声明所不允许抛出的异常时, unexpected函数会被调用,该函数的默认行为是调用terminate函数中止程序。用户也可以定义自己的unexpected函数,替换默认函数。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
class dividebuzeroexception{
char* msg;
public:
dividebuzeroexception(char* msg){
printf("dividebuzeroexception constructor....\n");
this->msg=msg;
}
dividebuzeroexception(const dividebuzeroexception& e){
printf("dividebuzeroexception copy constrcutor....\n");
this->msg=e.msg;
}
char* what(){
return msg;
}
};
int divide(int a,int b) throw(int){
double x=9;
if(b==0){
throw x;
}
return a/b;
}
void myunexpeted(){
printf("unexpected expected...\n");
throw 0;
}
int main(){
set_unexpected(myunexpeted);
while(1){
int a,b;
printf("input two numbers:\n");
cin>>a>>b;
try{
printf("%d/%d=%d\n",a,b,divide(a,b));
}catch(int e){
printf("%d is divide by zero!\n",e);
}catch(dividebuzeroexception& e){
printf("%s",e.what());
}
}
}
3.异常处理中的构造与析构
C++异常处理的真正功能,不仅在于它能够处理各种不同类型的异常,还在于它具有为异常抛出前构造的所有局部对象自动调用析构函数的能力。这一过程称为栈的解旋。
++函数后面加关键字throw(something)限制,是对这个函数的异常安全作出限制;这是一种异常规范,只会出现在声明函数时,表示这个函数可能抛出任何类型的异常。
(1)void fun() throw(); //表示fun函数不允许抛出任何异常,即fun函数是异常安全的。
(2)void fun() throw(…); //表示fun函数可以抛出任何形式的异常。
(3)void fun() throw(exceptionType); // 表示fun函数只能抛出exceptionType类型的异常。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
class MyException{
public:
MyException(const char* msg ){
printf("Constructor of MyException...\n");
strcpy(this->msg, msg);
}
MyException(const MyException& e){
printf("Copy Constructor of MyException...\n");
strcpy(this->msg, e.msg);//getMessage());
}
const char* getMessage() const {
return msg;
}
~MyException(){
printf("Destructor of MyException...\n");
}
private:
char msg[100];
};
class Demo {
public:
Demo(){
printf("Constructor of Demo\n");
}
~Demo(){
printf("Destructor of Demo\n");
}
};
void func() throw (MyException ){
Demo d;
printf("Throw MyException in func()\n");
throw MyException("exception thrown by func()");
}
int main() {
try{
func();
}catch(MyException e){
printf("Caught an exception: %s, addr=%x\n", e.getMessage(), (unsigned int)(long) &e);
}
printf("Resume the execution of main()\n");
return 0;
}
Constructor of Demo
Throw MyException in func()
Constructor of MyException…
Destructor of Demo
Copy Constructor of MyException…
Caught an exception: exception thrown by func(), addr=c9cdc7e0
Destructor of MyException…
Destructor of MyException…
Resume the execution of main()
标准库函数异常处理![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/1f201cb4198e8f9421532ac781f4f39e.gif)
4.类型转换
(1)动态转换:dynamic_cast<子类>(指针);//,需要父类是多态,即父类有虚函数
转换会有风险。
主要用于类的类型转换
由基类指针或引用向子类指针或引用的转换
使用·多态类型转换的要求:一定要是多态的,必须要有虚函数
dynamic_cast 转换失败返回NULL
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
class Base { //父类是多态
public:
virtual void func() {
printf("Base...\n");
}
virtual ~Base() {
} //最好加上析构的虚函数,方便释放子类的一些指针等
};
class Derive: public Base {
public:
void func() {
printf("Derive...\n");
}
void test() {
printf("test...\n");
}
};
int main() {
while (1) {
printf("1. create Base. 2.create son:\n");
Base *bp; //父类指针
int choice;
cin >> choice;
switch (choice) {
case 1:
bp = new Base;
break;
case 2:
bp = new Derive;
break;
}
Derive *dp = dynamic_cast<Derive*>(bp); //将父类指针转化成子类的指针
if (dp == NULL) { //如果bp原来是指向父类对象,转型失败
printf("case fail\n");
} else { //如果bp本来是指向子类对象,转换成功
dp->test();
}
}
return 0;
}
(2)静态转换:static_cast<>();//也是将父类指针转换成子类,但编译器不会判断,需要用户自行思考使用。多继承情况下,指针将会不一样。//得到的地址与原地址不一样
等同于C语言的强制转换
(3)const_cast<>();//将const于非const之间进行转换
(4)reinterpret_cast<>();//标准转换运算符
字符串无字节序
5编写一个计算三角形面积的函数,函数的参数为三角形三边边长a, b, c,可以用海伦公式计算:
在计算三角形面积的函数中需要判断输入的参数 a, b, c是否构成一个三角形,若不能构成,则需要抛出异常。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
double area(double a, double b, double c){
if(a <= 0 || b <= 0 || c <= 0){
throw invalid_argument("the side length should be positive!\n");
}
if(a+b <= c || a+c <= b || b+c <= a){
throw invalid_argument("the side length should be positive!\n");
}
double p = (a+b+c)/2;
return sqrt(p * (p-a) * (p-b) * (p-c));
}
int main() {
double a, b, c;
printf("Please input the side length of a triangle:\n");
cin >> a >> b >> c;
try{
double s = area(a, b, c);
printf("Area: %f\n", s);
}catch(exception& e){
printf("Error: %s\n", e.what());
}
return 0;
}