C++异常处理assert,throw,exit用法

本文详细介绍了编程中使用断言和异常处理的方法,包括如何在函数中检查参数合法性,通过assert函数实现对表达式的判断,以及在C++中使用throw和catch语句进行异常捕获和处理。此外,还阐述了exit函数的作用及其与return的区别,并讨论了在不同场景下合理使用这些机制的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常见的几个小细节问题。

  • assert应用:

      在现实世界中,我们脑袋时刻都在判断对与错,对的事情我们会继续深入下去,而错的事情我们会马上停止,那么在编程开发中我们如何赋予程序这种判断事物对错的能力呢?其中一个方案就可以使用断言assert,我们最常用的地方就是在函数中检查形参的数据合法性。

1、函数所属头文件:

        assert.h

2、函数原型:

        void assert (int expression);

3、功能说明:

        assert的关键在于判断expression的逻辑真假,如果为false,就会在stderr上面打印一条包含“表达式,文件名,行号”的错误信息,然后调用abort结束整个程序。

4、程序实例:

#include <stdio.h>
#include <stdlib.h>
//#define NDEBUG        //可以禁止断言
#include <assert.h>

void main()
{
    //测试true情况:以写打开一个文件,不存在则创建新文件
    FILE *fpWrite = fopen("d:\\testWrite", "w");

    //不会出错
    assert(fpWrite != NULL);

    fclose(fpWrite);

    //测试false情况:以读打开一个文件,不存在会失败
    FILE *fpRead = fopen("d:\\testRead", "r");

    //会出错
    assert(fpRead != NULL);

    //程序调用abort退出,不会执行到此步
    fclose(fpRead);
    system("pause");
}

结果:

    当然,频繁的调用assert会影响系统性能,增加系统额外的开销,如果想禁用断言功能时,可以在assert.h头文件之前定义NDEBUG。

 

  • throw应用:

出错是时可以用throw,然后在catch里面处理,
asert只是一个debug的检查,检查条件的真假,在release下asert语句不会被调用。

    抛出异常(也称为抛弃异常)即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:
throw 表达式;
    如果在try语句块的程序段中(包括在其中调用的函数)发现了异常,且抛弃了该异常,则这个异常就可以被try语句块后的某个catch语句所捕获并处理,捕获和处理的条件是被抛弃的异常的类型与catch语句的异常类型相匹配。由于C++使用数据类型来区分不同的异常,因此在判断异常时,throw语句中的表达式的值就没有实际意义,而表达式的类型就特别重要。

    eg:除数为0的异常可以用try/catch语句来捕获异常,并使用throw语句来抛出异常,从而实现异常处理,实现代码:

#include <iostream>
using namespace std;
double fuc(double x, double y)
{
    if (y == 0)
    {
        throw y;
    }
    return x / y;
}
void main()
{
    double res;
    try
    {
        res = fuc(2, 3);
        cout << "the result of x/y is" << res << endl;
        res = fuc(4, 0);
    }
    catch (double)
    {
        cerr << "error of dividing zero.\n";
        exit(1);
    }    
}

1、基础介绍

try
{
    //程序中抛出异常
    throw value;
}
catch (valuetype v)
{
    //例外处理程序段
}

语法小结:throw抛出值,catch接受,当然,throw必须在“try语句块”中才有效。

2、深入throw:
(i)、程序接受到throw语句后就会自动调用析构器,把该域(try后的括号内)对象clean up,然后再进
入catch语句(如果在循环体中就退出循环)。

这种机制会引起一些致命的错误,比如,当“类”有指针成员变量时(又是指针!),在 “类的构建器
”中的throw语句引起的退出,会导致这个指针所指向的对象没有被析构。这里很基础,就不深入了,提
示一下,把指针改为类就行了,比如模板类来代替指针,在模板类的内部设置一个析构函数。

(ii)、语句“throw;”抛出一个无法被捕获的异常,即使是catch(...)也不能捕捉到,这时进入终止函数
,见下catch。

3、深入catch:
一般的catch出现的形式是:
try{}
catch(except1&){}
catch(except2&){}
catch(...){} //接受所有异常
一般都写成引用(except1&),原因很简单,效率。

问题a:抛出异常,但是catch不到异常怎么办?(注意没有java类似的finally语句
在catch没有捕获到匹配的异常的时候,会调用默认的终止函数。可以调用set_terminate()来设置终止函数,参数是一个函数指针,类型是:void (*terminate)()。

到这里,可以题个问题:“没有try-catch,直接在程序中"throw;",会怎么样?”

其他一些技巧:
4、try一个函数体,形式如下

void fun(type1,type2) try----try放在函数体后
{
   函数定义
}
catch(typeX){}
这个用法的效果就相当于:
void fun() 
{
   try{函数定义}
}

5、throw一个函数体,形式如下:

void fun (); // 能抛出任何类型的异常
void fun () throw(except1,except2,except3) 
               // 后面括号里面是一个异常参数表,本例中只能抛出这3中异常
void fun () throw()   // 参数表为空,不能抛出异常

问题b:假设fun()中抛出了一个不在“异常参数表”中的异常,会怎么样?

答:调用set_terminate()中设定的终止函数。然而,这只是表面现象,实际上是调用默认的unexpected()函数,然而这个默认的unexpected()调用了set_terminate()中设定的终止函数。可以用set_unexpected()来设置 unexpected,就像set_terminate()一样的用法,但是在设定了新的“unexpected()”之后,就不会再调用 set_terminater中设定的终止函数了。

这个语法是很有用的,因为在用别人的代码时,不知道哪个地方会调用什么函数又会抛出什么异常,用一个异常参数表在申明时限制一下,很实用。

  • exit()应用:

函数: exit()

函数名: exit()
所在头文件:stdlib.h(如果是”VC6.0“的话头文件为:windows.h)
功 能: 关闭所有文件,终止正在执行的进程。
exit(1)表示异常退出.这个1是返回给操作系统的。
exit(x)(x不为0)都表示异常退出;
exit(0)表示正常退出。
exit()的参数会被传递给一些操作系统,包括UNIX,Linux,和MS DOS,以供其他程序使用。
stdlib.h: void exit(int status);
参 数 : status //程序退出的返回值
 
exit()和return的区别:
按照ANSI C,在最初调用的main()中使用return和exit()的效果相同。
但要注意这里所说的是“最初调用”。如果main()在一个递归程序中,exit()仍然会终止程序;但return将
控制权移交给递归的前一级,直到最初的那一级,此时return才会终止程序。return和exit()的另一个区别
在于,即使在除main()之外的函数中调用exit(),它也将终止程序。
_exit()与exit的区别:
头文件:
exit:#include<stdlib.h>
_exit:#include<unistd.h>
_exit()函数:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;
exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。
exit()函数与_exit()函数最大的区别就在于 exit()函数在调用 exit 系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件。

 

<think>好的,我现在需要帮助用户解决关于C++中空指针异常处理的问题。首先,用户提到在Java中可以捕获空指针异常,但C++的情况不同。用户已经给出了一段测试代码,并询问在C++中如何处理类似的问题。我需要先理解C++异常处理机制,以及为什么这段代码在C++中可能无法捕获空指针异常。 首先,回顾用户提供的代码示例。在C++中,解引用空指针(如*p)是未定义行为(UB),这意味着编译器不保证任何特定的行为,包括抛出异常。这Java不同,Java在遇到空引用时会明确抛出NullPointerException。因此,在C++中,尝试捕获这样的错误可能需要不同的方法。 接下来,我需要整理C++处理空指针问题的常见解决方案。用户可能想知道如何预防空指针解引用,或者在发生此类错误时如何检测和处理。可能的解决方法包括: 1. **防御性编程**:在解引用指针前检查是否为nullptr。这是最直接的方法,但需要程序员在每次使用指针时都进行检查,可能繁琐但有效。 2. **智能指针**:使用unique_ptr或shared_ptr等智能指针,它们可以自动管理内存,减少裸指针的使用,从而降低空指针的风险。但智能指针本身也可能持有nullptr,所以仍然需要检查。 3. **断言**:在调试阶段使用assert宏检查指针有效性,帮助在开发阶段发现问题。但发布版本中通常禁用断言,因此不作为运行时处理手段。 4. **自定义异常**:主动检查指针并在发现nullptr时抛出自定义异常,这样可以在上层通过try-catch块捕获。这需要显式地添加检查代码,但提供了结构化的错误处理方式。 5. **系统级信号处理**:例如,在Linux中使用信号处理函数捕获SIGSEGV信号(段错误)。但信号处理是全局的,且不可移植,不同系统可能有不同处理方式,通常不建议作为主要解决方案。 另外,需要指出C++标准中空指针解引用不会抛出异常,因此无法像Java那样通过catch(...)捕获。用户提供的示例代码中,解引用空指针会导致未定义行为,程序可能直接崩溃而不是进入catch块。 在参考资料中,用户引用了关于Java和C++空指针异常的内容。需要强调C++Java在异常处理机制上的差异,避免用户混淆两者。 最后,生成相关问题时要围绕用户的问题扩展,可能涉及其他类型的指针错误、智能指针的使用细节、调试技巧等。</think>### C++ 空指针异常处理解决方案 在 C++ 中,**解引用空指针是未定义行为**(Undefined Behavior),不会自动触发异常,因此无法像 Java 那样直接捕获空指针异常[^1]。但可以通过以下方法预防或处理空指针问题: --- #### 1. **防御性编程(显式检查指针)** 在解引用指针前强制检查是否为 `nullptr`: ```cpp void safe_access(int* p) { if (p == nullptr) { throw std::invalid_argument("Pointer is null!"); } int value = *p; // 安全解引用 } ``` --- #### 2. **使用智能指针(自动管理资源)** 通过 `std::unique_ptr` 或 `std::shared_ptr` 减少裸指针的使用: ```cpp #include <memory> void smart_pointer_demo() { auto ptr = std::make_unique<int>(42); if (ptr) { // 检查是否有效 int value = *ptr; // 安全访问 } } ``` --- #### 3. **断言调试(开发阶段检测)** 使用 `assert` 在调试阶段捕获空指针: ```cpp #include <cassert> void assert_demo(int* p) { assert(p != nullptr && "Pointer cannot be null!"); int value = *p; } ``` --- #### 4. **自定义异常(结构化错误处理)** 手动抛出异常并捕获: ```cpp #include <stdexcept> void custom_exception(int* p) { try { if (p == nullptr) { throw std::runtime_error("Null pointer encountered"); } int x = 100 + *p; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } } ``` --- #### 5. **系统级信号处理(不推荐)** 通过信号处理捕获段错误(不可移植): ```cpp #include <csignal> #include <iostream> void signal_handler(int signal) { std::cerr << "Segmentation fault detected!" << std::endl; exit(1); } int main() { signal(SIGSEGV, signal_handler); int* p = nullptr; int x = *p; // 触发信号处理 } ``` --- ### 关键区别:C++ Java 的空指针处理 | 特性 | C++ | Java | |--------------------|------------------------------|-----------------------------| | 空指针解引用 | 未定义行为(程序可能崩溃) | 抛出 `NullPointerException` | | 异常捕获 | 需手动检查并抛出异常 | 自动抛出并支持捕获 | | 推荐解决方案 | 智能指针 + 防御性检查 | 异常处理 + 代码规范 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值