【问题标题】:C++ exception for null pointers空指针的 C++ 异常
【发布时间】:2011-03-11 10:50:28
【问题描述】:

我有一个小函数(在 DLL 中),如下所示:

int my_function(const char* const s)
{
    try {
        return my_object->do_something_with(s);
    } catch (exception& e) {
        return ERROR_CODE;
    }
}

我认为 try-catch 块会阻止 my_object 内部可能发生的任何事情传播到外部。不幸的是,我错了,我调用这个函数(来自 VB)的程序刚刚停止工作,因为我传递了一个空指针参数。

那么,为什么我的 try-catch 块没有按(我)预期的那样工作?有解决方法吗?我曾经用 Java 编写过很多程序,我认为它会在那里工作......

【问题讨论】:

  • 那是你直接暴露给VB的函数吗?你没有COM接口吗?
  • 不,我只是导出了一些可以用__declspec(dllexport) __stdcall和一个DEF文件调用的函数。
  • 好吧,那么您必须选择一些值来指示错误。您必须手动检查空指针并返回该值。 C++ 异常在这里帮不了你。

标签: c++ exception-handling nullpointerexception


【解决方案1】:

在 Java 中工作,因为在 Java 中会产生 NullPointerException。在 C++ 中,没有这种普遍存在的异常,取消引用空指针只会导致分段错误。

此外,在 C++ 中没有强制的异常类型层次结构。可以抛出任何类型的异常;异常不必源自std::exception。 (因此,即使有人定义了 class NullPointerException {};,但没有指定它从 std::exception 派生,您仍然不会发现它。)

【讨论】:

  • 谢谢。所以防止它的唯一方法是首先检查== 0的对象?另外,我可以在 catch 括号中写些什么来捕捉任何东西吗?
  • 是的,可以捕获catch (...) 构造引发的任何异常,但您可能不应该这样做。显然,使用此构造,您无法访问异常对象,甚至无法确定其类型,因此您能否使用它来正确处理异常是值得怀疑的。最后,这仍然不会使您在取消引用空指针时避免程序出现分段错误。
【解决方案2】:

在 C++ 中取消引用空指针是未定义的行为。这意味着 C++ 标准对可能发生的事情没有任何限制。任何事情都是允许的——一个特定的实现可能会记录它自己的行为,也可能不会。如果不是,则行为可能是一致且可预测的,也可能不是。

一般来说,最好假设未定义的行为会导致您的计算机着火(或格式化硬盘)。只是不要激怒它。当然,在你的代码的第一次剪切中很难避免错误,所以当你意外地这样做并且你的计算机没有着火时,那么你很幸运,修复了错误并继续前进。你的函数可以重写:

int my_function(const char* const s)
{
    if (s == 0) return ERROR_CODE;
    return my_object->do_something_with(s);
}

或者,您可以先修复调用者不传递空指针,或者您可以修复do_something_with 以接受空指针。带有指针参数的 C++ 函数应记录是否允许使用空指针。

不同的人会以不同的方式编写测试(s==0) - 它可能是(s==NULL)(NULL==s)(!s)。选择一个你喜欢的。

当然,在实践中,编译器有充分的理由不让您的计算机着火,而现代操作系统试图确保行为不佳的应用不会给其他应用带来巨大的不便。因此,在大多数系统上,大多数时候,当您取消引用空指针时,您会收到某种硬件异常或信号,或者您的进程将被终止,或者在嵌入式系统上,设备可能会锁定并需要重新启动.

一般情况下,您不会获得可以使用任何类型的 catch 子句捕获的 C++ 风格的软件异常,尽管 Windows 确实有一个称为 SEH 的功能,您可以使用它来获得该行为。

【讨论】:

  • 这是我第一次看到有人提到“未定义的行为”而没有提到“鼻恶魔”;)
【解决方案3】:

在 C++ 中,可以抛出任何东西,并且任何东西不一定来自 std::exception,我假设你正在捕捉它(使用命名空间 std?)。

如果您想捕获可能被抛出的所有东西,您可以使用 catch-all,catch (...)。这将捕获任何异常,但您不会知道它捕获的异常是什么。在可能的情况下,最好捕获特定的异常。

请注意,通常情况下,catch all 不会处理较低级别的异常,例如访问冲突。如果您使用的是 Windows 和 Visual Studio,则可以将项目配置为使用 /EHa 使 catch-all 块处理这些异常,但这样做通常很危险,不建议这样做。

【讨论】:

    【解决方案4】:

    c++ faq items regarding exceptions 可能会帮助您更好地理解 c++ 异常。

    很多用java编程的人在理解指针和引用方面存在问题,所以如果程序停止工作,可能是由于崩溃(由于取消引用NULL导致的分段错误?)

    【讨论】:

    • 更正 - 指针和引用有问题的不是 Java 人。是用 Java 编程的人(或任何其他解释为 C# 或 PHP)的人,仅此而已,而且他们从未在任何其他给你讨厌的 SEGFAULT(即 C、C++、Pascal)和/或的东西上工作过从来没有一个好的CS基础。所以它不是 Java People 而是 Java People 中的某个部门(我很遗憾承认这是一个很大的部门。)
    【解决方案5】:

    C++ 中没有 NullPointerException。如果你真的想捕捉这样的访问,你必须捕捉一些信号(例如 SIGSEGFAULT)。

    【讨论】:

    • 我猜他说的是一个 DLL,它很可能是 Win32,因此不太可能导致分段错误。
    【解决方案6】:

    持保留态度,因为自 1999 年以来我没有接触过 C++ 编译器。STL 和 C++ 发生了很大变化,也许事情已经发生了变化。不过,这是我记得的。

    C++ 中的异常模型与 Java 中的异常模型非常不同。在检查错误(以及抛出您自己的自定义异常)时,您必须更加积极主动,但要更加积极主动。

    在 Java 中,取消引用引用的调用最终是由 JVM 发送和解释的字节码指令。 JVM 总是(在内部)检查引用是否为 null ,当它发现是 null 时,它会自动抛出 java.lang.NullPointerException。

    如果 JVM 没有被编程来做这样的事情,对空引用的访问会导致 JVM 因 SEGFAULT 而崩溃——这正是当遇到未受保护的空指针时使用已编译的、未受保护的语言所期望的行为取消引用(不仅是 C 或 C++,而且在 Pascal、Ada、Assembly 甚至是好的旧 BASIC 中都试图在无效的地址位置进行 PEEK/POKE。)

    在 C++(或我提到的任何语言中)中(至少在我的时代)的方式是显式检查指针(或分配给引用的地址)是否为空。此时,如果测试返回 true,则启动异常。你不会盲目地追随指针。在取消引用之前,您必须明确测试它是否为空(或有一个非常强烈保证的前提条件,该指针实际上指向您需要访问的东西) .

    这有点等价于下面的 Java 伪代码

    if( myPossiblyBadReference == null )
    {
       throw java.lang.NullPointerException( "you suck!" );
      // or throw my.own.NPEException(); // preferably a specialization of NullPointerException
    }
    else
    {
       doYourThingieWith( myPossiblyBadReference );
    }
    

    现在在 C++ 中可能会做不同的事情,特别是现在 C++0x 的东西从管道中传出(我对此一无所知。)但至少,这是我在编码时必须处理的行为C++。

    换句话说,完成这些事情涉及更多的肘部油脂。请记住,您是在 JVM 提供的抽象级别较低的抽象级别上工作的。

    JVM 是一个屏障,它为您提供了许多出色的错误处理功能(我们 Java 开发人员往往过于依赖这一功能。)当您在较低的环境下工作时,需要明确地对这些错误处理功能进行编码级别(不只是在 C++ 中。)

    【讨论】:

      猜你喜欢
      • 2015-07-12
      相关资源
      最近更新 更多