cpp异常捕获的使用

⌚Time: 2024-01-16 18:35:27

👨‍💻Author: Jack Ge

c++异常捕获

在C++中,异常可以通过try-catch语句块来捕获和处理。try块中的代码可能会抛出异常,catch块会捕获并处理这些异常。

语法如下:


try {

   // 可能会抛出异常的代码

}

catch (ExceptionType1 e) {

   // 处理ExceptionType1类型的异常

}

catch (ExceptionType2 e) {

   // 处理ExceptionType2类型的异常

}

...

catch (...) {

   // 处理其他类型的异常

}

在try块中,我们编写可能会抛出异常的代码。如果发生了异常,程序会立即跳转到catch块中执行对应的代码。

catch块中的参数指定了要捕获的异常类型。如果捕获到与指定类型匹配的异常,程序将执行对应的catch块中的代码。可以有多个catch块,分别处理不同类型的异常。

最后一个catch块使用省略号(...)来捕获其他类型的异常,它会捕获所有其他类型的异常。通常情况下,建议将这个catch块放在最后,以确保所有的异常都能被捕获到。

以下是一个示例:


#include <iostream>



int main() {

   try {

      int age;

      std::cout << "请输入年龄: ";

      std::cin >> age;



      if (age < 0) {

         throw "年龄不能为负数";

      }



      std::cout << "年龄是: " << age;

   }

   catch (const char* msg) {

      std::cout << "捕获到异常: " << msg;

   }



   return 0;

}

在这个示例中,程序先读取用户输入的年龄,然后检查年龄是否小于0。如果年龄小于0,则抛出一个C风格字符串异常。

catch块中捕获到异常后,会输出相应的错误信息。

当然,在实际应用中,我们可以自定义异常类型,并在catch块中处理它们。这样可以根据不同的异常类型,执行不同的处理逻辑。

异常传递

C++中的异常传递是指在一个函数中抛出的异常可以被上层调用函数的异常处理机制捕获和处理。

当一个函数抛出异常时,如果没有在当前函数中捕获并处理该异常,异常会被传递给调用该函数的上层函数,继续寻找可以处理该异常的代码。如果异常一直没有被上层函数捕获并处理,最终会导致程序终止,并打印出未捕获的异常信息。

在C++中,可以使用throw语句来抛出异常,并将异常传递给上层代码进行处理。

以下是一个简单的示例:


#include <iostream>

#include <stdexcept>



void processInput(int value) {

    if (value < 0) {

        throw std::invalid_argument("Input value cannot be negative.");

    }

    // 其他处理逻辑...

}



int main() {

    try {

        int inputValue;

        std::cout << "Enter a positive integer: ";

        std::cin >> inputValue;



        processInput(inputValue);

        

        // 其他操作...

    } catch (const std::exception& e) {

        std::cout << "Exception caught: " << e.what() << std::endl;

        // 异常处理逻辑...

    }



    return 0;

}

在上面的示例中,processInput函数根据输入值是否为负数来抛出异常。在主函数中,通过try-catch块捕获异常,并进行相应的处理。

当输入的值为负数时,processInput函数会抛出一个std::invalid_argument异常,然后由catch块捕获并处理该异常。在异常处理块中,可以根据需要输出异常信息,并进行适当的操作。

注意:在抛出异常时,可以使用不同类型的异常对象,以及自定义的异常类对象。在catch块中,可以匹配不同类型的异常,以执行不同的异常处理逻辑。

函数定义中使用throw关键字

在C++中,可以在函数定义中使用throw关键字指定函数可能抛出的异常类型。

以下是一个示例:


#include <iostream>

#include <stdexcept>



void processInput(int value) throw(std::invalid_argument) {

    if (value < 0) {

        throw std::invalid_argument("Input value cannot be negative.");

    }

    // 其他处理逻辑...

}



int main() {

    try {

        int inputValue;

        std::cout << "Enter a positive integer: ";

        std::cin >> inputValue;



        processInput(inputValue);

        

        // 其他操作...

    } catch (const std::exception& e) {

        std::cout << "Exception caught: " << e.what() << std::endl;

        // 异常处理逻辑...

    }



    return 0;

}

在上面的示例中,processInput函数通过在函数定义中使用throw关键字指定了可能抛出的异常类型为std::invalid_argument。这样,在函数实现中使用throw语句抛出的异常类型必须与函数定义中指定的异常类型相匹配。


void GetTag() throw(int);                       // 表示只抛出int类型异常

void GetTag() throw(int,char);              // 表示抛出in,char类型异常

void GetTag() throw();                          // 表示不会抛出任何类型异常

void GetTag() throw(...);                       // 表示抛出任何类型异常

使用函数声明中的throw关键字可以帮助提供更详细的函数接口信息,指示函数可能产生哪些异常,从而帮助调用者更好地处理可能发生的异常情况。

异常捕获作用

C++异常捕获的作用在于处理程序在运行过程中可能出现的异常情况,保证程序的稳定性和可靠性。以下是异常捕获的几个重要作用:

  1. 异常处理:通过捕获异常并进行相应的处理,程序可以在异常发生时采取措施以确保程序的正常执行。例如,在文件操作中如果出现了文件打开失败的异常,可以在catch块中关闭文件和清理资源,避免资源泄漏。

  2. 错误传递:当程序中的一部分无法处理某个异常时,它可以将异常抛出,并由上层的代码来处理。这种方式可以将错误信息传递给更高层的代码,在上层进行更合适的处理。

  3. 异常恢复:在一些情况下,程序可能可以通过捕获异常并执行一些修复操作来恢复正常状态。例如,在网络通信过程中,程序可以捕获网络连接异常,并尝试重新连接或者恢复网络连接。

  4. 程序调试:当程序发生异常时,异常捕获可以提供有关异常发生位置和原因的信息,帮助程序员进行调试和修复问题。异常捕获可以显示错误信息,并可以通过栈回溯(stack trace)提供异常发生的调用栈信息。

总结起来,异常捕获的作用是处理和管理程序中的异常情况,以保证程序的正常执行和可靠性。异常捕获使得程序具有了更好的健壮性和容错性,能够适应各种异常情况并进行相应的处理。

自定义异常

在C++中,可以通过继承标准异常类std::exception来自定义异常。

下面是自定义异常的一个简单示例:


#include <exception>

#include <string>



class MyException : public std::exception {

private:

    std::string errorMessage;



public:

    // 构造函数,传入错误信息

    MyException(const std::string& message) : errorMessage(message) {}



    // 重写what函数,返回错误信息

    const char* what() const noexcept override {

        return errorMessage.c_str();

    }

};

在上面的示例中,我们自定义了一个名为 "MyException" 的异常类,它继承自std::exception。

我们可以在构造函数中传入错误信息,并通过重写what函数,返回错误信息。

然后,我们就可以在程序中抛出自定义异常,例如:


#include <iostream>



void myFunction() {

    throw MyException("This is a custom exception message.");

}



int main() {

    try {

        myFunction();

    } catch (const MyException& e) {

        std::cout << "Exception caught: " << e.what() << std::endl;

    }



    return 0;

}

在上面的示例中,我们在myFunction函数中抛出了自定义异常(MyException),然后在主函数中使用try-catch块捕获并处理该异常。捕获到的异常对象可以通过e.what()来获取异常的错误信息。

注意:自定义异常类应该继承自std::exception或其派生类,以便能够与标准异常处理机制兼容。

抛出异常后代码的执行顺序

简单的测试


// Example program

#include <iostream>

class AAA{

public:

    AAA(){//构造函数

        std::cout << "ccc" << std::endl;//执行

    }

    ~AAA(){//析构函数

        std::cout << "ddd" << std::endl;//执行

    }

    void func(){

        std::cout << "fff" << std::endl;//没有执行

    }

};

int main(void)

{

    AAA a;

    try{

        throw("error");//抛出异常

        std::cout<<"ttt"<<std::endl;//没有执行

        a.func();//没有执行

    }

    catch (const char* e){

        std::cout << e << std::endl;//执行

    }

    catch (...){

        std::cout << "..." << std::endl;//没有执行

    }

    std::cout << "mmm"<<std::endl;//执行

    return 0;

}

结果


ccc

error

mmm

ddd



异常在抛出后跳过所有调用的函数后面的语句,直到catch块捕获,这样就可以避免意外而导致的程序未知运行错误。如果没有被捕获处理会终止程序运行。


#include <iostream>

#include <string>



void func1(){

    throw("aaa");

    std::cout<<"func1\n";

}

void func2(){

    func1();

    std::cout<<"func2\n";

}

void func3(){

    func2();

    std::cout<<"func3\n";

}

int main(void)

{

    try{

        func3();

    }

    catch (const char* e){

        std::cout << e << std::endl;

    }

    catch (...){

        std::cout << "..." << std::endl;

    }

    std::cout << "main"<<std::endl;

    return 0;

}

结果


aaa

main