moc 发表于 2018-9-13 22:12:37

041-C++之异常处理

本帖最后由 moc 于 2018-9-13 22:12 编辑

1、异常的简介
    异常是程序在执行期间产生的问题,提供的一种转移程序控制权的机制,与函数机制独立和互补。
函数是一种以站结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它依附于栈的结构,却可以同时设置多个异常作为网捕条件,以类型匹配在栈机制中跳跃回馈。
    C++ 异常处理涉及到三个关键字:try、catch、throw。
异常处理的基本思想:
       
1. C++的异常处理机制使得异常的引发和异常处理不必在一个函数中,这样底层函数可以着重解决具体的问题,而不必过多的考虑异常的处理。上层调用者可以再适当的位置设计针对不同类型的异常处理。
2. 异常是专门针对抽象编程的一系列错误处理,C++中不能借助函数机制,因为栈结构的本质是先进后出,依次访问,无法跳跃,但错误处理机制遇到错误可以转到若干级之上进行错误处理。

3. 异常超脱于函数机制,决定了其对函数的跨越式回跳。即异常跨越函数。
2、异常的基本语法
throw表达式;    // 抛出异常
//捕获并处理异常
try
{
   // 保护代码
}catch( ExceptionName e1 )
{
   // catch 块
}catch( ExceptionName e2 )
{
   // catch 块
}catch( ... )//其他未知异常
{
   // catch 块
}
1.若有异常则通过throw操作创建一个异常对象并抛掷;
2.将可能抛出的异常的程序嵌在try块中,try块内为保护代码。
3.catch子句在try之后,按顺序进行检查,匹配捕获并进行异常处理(或继续抛异常);
4.try语句没有发生异常,则跳过catch子句继续向下执行;
5.如果没有找到匹配的catch,即抛出的异常无人接受,激发最后一道防线,terminate函数(可修改),该函数引起运行终止和abort函数。
6. 类的构造函数无返回类型,不能通过返回值报告运行状态,可通过异常解决。
7. 异常机制与函数机制互不干涉,异常的捕捉方式是基于类型匹配,捕捉相当于函数返回类型匹配,而不是函数参数匹配,所以捕捉不用考虑一个抛掷中的多种数据类型匹配问题。
8. 异常匹配严格按照类型匹配。匹配苛刻程度与模板一样,不允许隐式类型转换。
void divide(int x, int y)
{
        if (y == 0)    // 0为除数
        {
                throw x;   // 抛出 int类型异常
        }
        cout << "Divide结果:" << x / y << endl;
}

void myDivide0(int x, int y)
{
        divide(x, y);
}

// 发生的异常必须在函数中处理,否者会产生函数中断
int main()
{
        try
        {
                myDivide0(10, 2);
                myDivide0(123, 0);
        }

        catch (int e)   // 捕获int型异常
        {
                cout << e << "被零除" << endl;
        }

        catch (...)   // 捕获漏网之鱼
        {
                cout << "其他未知的异常!" << endl;
                cout << "我接受了异常,但我没有处理,我继续往外扔";
                throw;
        }

        system("pause");
        return 0;
}
3、栈解旋
异常抛出后,从进入try块起到异常被抛出前,这期间在栈上的构造的所有对象都会被析构,析构的顺序和构造的顺序相反,这一过程即栈解旋 。
函数抛出异常限定,可以在定义函数之后加throw说明。
class Test
{
public:
        Test(int a = 0, int b = 0)
        {
                this->a = a;
                this->b = b;
                cout << "构造函数do\n";
        }
        ~Test()
        {
                cout << "析构函数do\n";
        }
private:
        int a;
        int b;
};

void myDivide()
{
        Test t1(1, 2), tr2(3, 4);// 测试t1,t2的析构顺序
        cout << "myDivide...发生异常\n";
        throw Test();
}

void myDivide12() throw (int , char, char*)//该函数只能抛出int,char,char*类型的异常
{
        throw Test();
}

void myDivide2() throw()// 该函数不会抛出任何异常
{
        throw Test();// 非要抛出时有警告。
}

int main()
{
        try
        {
                myDivide();
        }
        catch (int a)
        {
                cout << "int类型 异常\n";
        }
        catch (...)
        {
                cout << "未知 异常\n";
        }

        system("pause");
        return 0;
}
4、异常变量的生命周期
throw “aaa”;是抛出的char*类型。
对于抛出类对象:
1.如果接受异常的时候使用一个异常变量,则执行copy构造函数构造异常变量;
2.使用引用的话会直接使用throw时候的那个对象
3.使用指针接的话,如果抛出时不new一个对象的话,会形成参野指针;
最终结论:类对象发生异常时,使用引用比较合适
class BadSrcType {};
class BadDestType {};
class BadProcessType
{
public:
        BadProcessType()
        {cout << "BadProcessType构造函数do\n";}

        BadProcessType(BadProcessType &obj)
        {cout << "BadProcessType拷贝函数do\n";}

        ~BadProcessType()
        {cout << "BadProcessType析构函数do\n";}
};

// throw类对象 类型异常
void my_strcpy(char* to, char*from)
{
        if (from == NULL)
        {
                throw BadSrcType();
        }
        if (to == NULL)
        {
                throw BadDestType();
        }

        // copy时 场景检查
        if (*from == 'a')
        {
                cout << "开始BadProcessType类型异常\n ";
                throw BadProcessType();
        }

        if (*from == 'b')
        {
                cout << "开始BadProcessType类型异常\n ";
                throw &(BadProcessType());
        }

        if (*from == 'c')
        {
                cout << "开始BadProcessType类型异常\n ";
                throw new BadProcessType;
        }

        while (*from != 0)
        {
                *to++ = *from++;
        }
        *to = '\0';
}


int main()
{
        int ret = 0;
        char buf1[] = "asdfghjkl";
        char buf2 = { 0 };

        try
        {
                my_strcpy(buf2, buf1);
        }
        catch (int e)
        {
                cout << e << "int类型异常\n";
        }
        catch (char* e)
        {
                cout << e << "char*类型异常\n";
        }
        catch (BadSrcType e)
        {
                cout << "BadSrcType类型异常\n";
        }
        catch (BadDestType e)
        {
                cout << "BadDestType类型异常\n";
        }
        //结论1:如果接受异常的时候使用一个异常变量,则执行copy构造函数构造异常变量
        catch (BadProcessType e)
        {
                cout << "BadProcessType类型异常\n";
        }
        //结论2:使用引用的话会使用throw时候的那个对象
        //catch (BadProcessType &e)
        //{
        //        cout << "BadProcessType类型异常\n";
        //}
        //结论3:指针可以和引用写在一块,但引用/元素不能写在一块
        catch (BadProcessType *e)
        {
                cout << "BadProcessType指针类型异常\n";
                delete e;
        }
        catch (...)
        {
                cout << "出现未知异常!\n";
        }
        // 最终结论:类对象发生异常时,使用引用比较合适
        system("pause");
        return 0;
}
5、异常的层次结构
即继承在异常中的应用
对于同一种类型的异常;异常派生,按引用传递异常。
6、C++ 标准的异常
        C++ 提供了一系列标准的异常,定义在 <exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的,如下所示:

传送门

页: [1]
查看完整版本: 041-C++之异常处理