C++之异常

错误处理的方法:
1.程序终止.(例如assert断言,Linux中的段错误等)
2.返回错误码.
3.返回一个合法值,不至于让程序崩溃.(例如:服务器程序)
4.异常,返回错误信息.
异常:当一个函数发现自己无法处理的错误时就抛出异常,让程序员可以直接或者间接的去处理这个问题.

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

int main()
{
    vector<int> v;
    try
    {
        v.at(0);   //越界访问
    }
    catch(exception &e)
    {
        cout<<e.what()<<endl;
    }
    return 0;
}

运行的结果:invalid vector subscript
抛出了异常.

异常的抛出和捕获

在一个程序中可以多次进行异常的捕获和抛出:
例如:

void fun1()
{
    try
    {
        vector<int> v;
        v.at(0);
    }
    catch(exception &e)
    {
        throw e;     //扔出异常给上一级 
    }
    cout<<"fun1()"<<endl;
}
void fun()
{
    fun1(); 
    cout<<"fun()<<endl;"
}
int main()
{
    try
    {
        fun();
    }
    catch(exception& e)
    {
        cout<<e.what()<<endl;
    }
    return 0;
}

结果
在这个例子中,异常在fun()函数和fun1()函数中并没有对捕获的异常进行处理,所以那两个打印都没有出现.只有异常被及时处理了才会执行后面的代码,否则就会进行跳转.一直调到main()函数,如果在main()函数中也没有进行处理,那么就会终止程序

总结:
1. 异常是通过抛出对象而引发的.并且与对象的类型有关.
2. 在捕获异常时,选择就近原则,捕获类型正确的就会被捕获.下限是main()函数.
3.当在catch()中throw一个异常时,当出了函数后,throw的生命周期就会结束.但是在上一个函数依然可以捕获到throw的异常,说明在传递异常时是通过拷贝一个临时对象来传递throw的异常.(在这可以类比前面的引用返回值)

异常的捕获匹配原则

1.允许非const对象到const对象的转换.
2.允许从派生类到基类的转换.
3.数组可以转化为指向数组的指针,函数可以转化为指向函数类型的指针.

下面是一个具体的实例:

class Exception
{
public:
    Exception(const string& _msg,int n)
        :msg(_msg)
        ,id(n)
    {}
    virtual string What()   //虚函数,构成多态
    {
        return msg;
    }
    int GetId()
    {
        return id;
    }
protected:
    string msg;
    int id;
};

class SqlException:public Exception
{
public:
    SqlException(const string& _msg,int n)
        :Exception(_msg,n)
    {}
    virtual string What()   //子类的重写
    {
        string m = "数据库错误:";
        m += msg;
        return m;
    }
};

class NetException:public Exception
{
public:
    NetException(const string& _msg,int n)
        :Exception(_msg,n)
    {}
    virtual string What()
    {
        string m = "网络错误:";
        m += msg;
        return m;
    }
};

class CacheException:public Exception
{
public:
    CacheException(const string& _msg,int n)
        :Exception(_msg,n)
    {}  
    virtual string What()
    {
        string m = "缓存错误:";
        m += msg;
        return m;
    }
};

void Connectsql()
{
    if(rand() % 7 == 0)
    {
        throw SqlException("数据库连接失败\n",7);
    }
}
void InitCache()
{
    if(rand() % 5 == 0)
    {
        throw CacheException("初始化缓存\n",5);
    }   
}
class Server
{
public:
    void StartSql()
    {
        Connectsql();
    }
    void StartCache()
    {
        InitCache();
    }
    void start()
    {
        while(1)
        {
            try
            {
                StartSql();
                StartCache();
                Sleep(100);
            }
            catch(Exception& e)   //传引用,构成多态
            {
                cout<<e.What()<<endl;
            }
            catch(...)
            {
                cout<<"未知异常.\n"<<endl;
            }
        }
    }
};
int main()
{
    srand(time(0));
    Server s;
    s.start();
    return 0;
}

异常的重新抛出

在上述的例子中,抛出的异常并不利于阅读,所以为了有更好的可读性,就可以再次封装一个类,重新抛出一个通俗易懂的异常.

异常与构造函数,异常与析构函数

构造函数的作用是初始化对象,但如果对象没有构造完成就抛出了一个异常,那么对象就会不完整了.
当构造函数完成后,如果在析构函数之前抛出了一个异常,那么就可能会造成不能析构.

例如:当new了一段空间,但是在delete的前面抛了一个异常,就会造成无法释放造成内存泄露.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值