bosthhe1 / cpushpush

0 stars 0 forks source link

抛异常 #48

Open bosthhe1 opened 1 year ago

bosthhe1 commented 1 year ago

异常在c++中经常使用,但是抛异常的时候需要注意异常规范首先我们先了解异常的书写以及一些基本特性

bosthhe1 commented 1 year ago

这是最简单的抛异常,当我们发现除零错误的时候,会直接跳转到最近一个匹配catch的地方,如果异常不捕获,就会终止程序

int func(int a, int b)
{
    if (a == 0 || b == 0)
    {
        string st("除零错误");
        throw st;
    }
    return a / b;
}

int main()
{
    try
    {
        func(1, 0);
    }
    catch (string& st)
    {
        cout << "捕获异常:" << st << endl;
    }
    catch (...)//这里是为了防止未知异常没有捕获而终止程序设计的,专门捕获没有未知的异常
    {
        cout << "未知异常" << endl;
    }
    return 0;
}
bosthhe1 commented 1 year ago

一般的异常都是子类和父类构成虚函数重写what(),然后所有的子类都抛给基类对象的引用,最后基类对象通过不同的虚基表指针指向不同的虚函数

namespace hxh{
    class exception
    {
    public:
        exception(string errmsg, int id)
            :_errmsg(errmsg)
            , _id(id)
        {}
        virtual string what()
        {
            string st;
            st += _errmsg;
            return st;
        }
    protected:
        string _errmsg;
        int _id;
    };
    class Httpserver :public exception
    {
    public:
        Httpserver(string errmsg, int id, string type)
            :exception(errmsg,id)
            ,_type(type)
        {
        }
        virtual string what()
        {
            string st(_errmsg);
            st += ":";
            st += _type;
            return st;
        }
    private:
        string _type;
    };
    class sqlexcetion:public exception
    {
    public:
        sqlexcetion(string errmsg, int id, string type)
            :exception(errmsg, id)
            , _type(type)
        {
        }
        virtual string what()
        {
            string st(_errmsg);
            st += ":";
            st += _type;
            return st;
        }
    private:
        string _type;
    };
}
void func()
{
    while (1)
    {

        try
        {
            srand(time(0));
            while (1)
            {
                Sleep(1000);
                if (rand() % 5 == 1)
                {
                    hxh::exception st("exception", 100);
                    throw st;
                }
                else if (rand() % 5 == 0)
                {
                    hxh::Httpserver st("Httpserver", 101, "权限不足");
                    throw st;
                }
                else if (rand() % 5 == 3)
                {
                    hxh::sqlexcetion st("sqlexcetion", 102, "shorage short");
                    throw st;
                }
            }
        }
        catch (hxh::exception& e)//至于这里的引用是已经析构的对象,所以编译器会另外开空间,将e的引用拿过去,所以这里没有出现越界问题
        {
            cout << e.what() << endl;
        }
        catch (...)
        {
            cout << "未捕获异常" << endl;
        }
    }

}
int main()
{
    func();
    return 0;
}
bosthhe1 commented 1 year ago

由于抛异常容易引发死锁问题,一般有两种解决方案,一种是guard_mutex,一种就是在抛异常的地方直接进行捕获,不跳到其他函数体中,这里是捕获的场景

void func1()
{
    mutex mtx;
    vector<int> v;
    try
    {
        thread td1([&]{
            for (int i = 0; i < 10000; i++)
            {
                mtx.lock();
                v.push_back(i);//假如在这里抛异常
                throw string("内存泄露");
                mtx.unlock();
            }
        });
        thread td2([&]{
            for (int i = 0; i < 10000; i++)
            {
                mtx.lock();
                v.push_back(i);
                mtx.unlock();
            }
        });
    }
    catch (...)
    {
        mtx.unlock();
        throw;//将异常原封不动的抛给下一个捕捉异常的地方
    }
}

int main()
{
    try
    {
        func1();
    }
    catch (string& st)
    {
        cout << st << endl;
    }
    catch (...)
    {
        cout << "未知异常" << endl;
    }
    return 0;
}
bosthhe1 commented 1 year ago

异常的优缺点 优点: 相比于错误码,异常可以更准确的显示错误信息。 相比于错误信息,错误信息的返回是层层返回的,而异常是直接跳转到捕获的地方 异常使用广泛,我觉得最重要的一点是异常不会终止程序,而错误信息会终止程序 缺点: 由于异常是跳转到捕获的地方,所以异常的调试跟踪比较困难 异常有一定的性能消耗,但是如今计算机对这种消耗忽略不计 异常一定要规范使用 异常用的不好容易造成死锁,内存泄露的问题 c++的异常定义不是很好,导致大家都是使用的自己定义的异常,非常混乱