c ++如何设置更严格的异常(错误)规则?

时间:2015-11-01 09:28:38

标签: c++ arrays exception visual-c++

此代码不会导致异常:

int *a=new int[100];  
try{
  a[101]=1;
}
catch(...){
//never called
}

但是,如果索引较大(a [11111111] = 1),则会发生错误。

是否有可能制定更严格的处理错误规则? (MSDN VC ++)

谢谢!

4 个答案:

答案 0 :(得分:3)

您似乎对例外有两种误解:

  1. C ++会抛出所有错误的异常,例如Java。
  2. C ++应该由程序员编写,以便为所有错误抛出异常。
  3. 两者都是误解。在C ++中,标准库组件或语言本身很少抛出异常,并且只应在问题很少但不受控制的情况下,或者在没有其他方法报告错误的情况下抛出异常。

    前者的一个例子是分配比系统能够提供的内存更多的内存。这是一个相对罕见的问题,外部资源无法阻止。因此,如果您的内存不足,new确实会抛出std::bad_alloc异常。

    后者的示例是构造函数和带引用的dynamic_cast

    在动态分配的数组中访问非法索引是异常的一个很好的用例,因为您可以轻松地防止该错误。如果有的话,它应该是一个断言,因为如果你访问非法索引,那么你有一个编程错误,即一个错误。 无法在运行时修复错误。

    处理此类低级错误的常用C ++方法是未定义的行为。在您的示例中:

    int *a=new int[100];  
    try{
      a[101]=1;
    }
    catch(...){
    //never called
    }
    

    访问a[101]的尝试调用未定义的行为,这意味着允许程序执行所有操作。它可能崩溃,它可能工作正常,甚至可能抛出异常。这完全取决于您的编译器和您的环境。

    当然,生活在未定义的行为中并不是一件好事,所以你想对它采取一些行动是可以理解的。

    首先,不要使用new[]。请改用std::vector。使用std::vector的非法元素访问仍然是未定义的行为,但是您的编译器可能会引入额外的运行时检查,以便未定义的行为导致立即崩溃,以便您可以修复该错误。

    std::vector 还有一个at成员函数,它会抛出错误索引的异常,但这比任何设计错误更多,因为在std::vector级别,错误的索引表示程序逻辑中更高抽象级别的问题。

    重要的是你放弃了能够处理"所有错误都一样。将您的错误分为三个不同的类别:

    1. 错误。错误意味着您的代码错误,并且您的程序不是您认为的那样。对这些错误使用assert和/或依靠编译器在C ++标准库组件中引入相应的检查。错误的代码应该快速崩溃,以便尽可能减少伤害。提示:MSVC有一些名为"调试版本"。 A"调试版本"是一组编译器和链接器选项,它允许进行大量额外的运行时检查,以帮助您找到代码中的错误。

    2. 错误的输入。所有程序都收到错误的输入。你绝不能假设输入是正确的。输入来自人类还是来自机器并不重要。错误的输入必须是正常程序流程的一部分。不要使用assert或错误输入的例外。

    3. 外部资源的异常状态。这是应该使用的异常。每个程序都依赖于一种形式的外部资源,通常由操作系统提供。主要的外部资源是内存。像std::vector<int> x(100);这样的东西通常不会失败,但理论上它可能,因为它需要记忆。启动新线程通常不会失败,但理论上,操作系统可能无法启动。这些都是特殊情况,因此异常是处理它们的好方法。

    4. 当然,这些是粗略的指导方针。在错误的输入和外部资源问题之间划出一条确切的界线尤其困难。

      不过,这是一个试图总结指南的例子:

      #include <iostream>
      #include <vector>
      #include <cstdlib>
      #include <cassert>
      
      void printElement(std::vector<int> const& v, int index)
      {
          // ----------------
          // Error category 1
          // ----------------
          assert(index >= 0);
          assert(index < static_cast<int>(v.size()));
          std::cout << v[index] << "\n";
      }
      
      int main()
      {
          std::cout << "Enter size (10-100): ";
          int size = 0;
          std::cin >> size;
          if (!std::cin || (size < 10) || (size > 100))
          {
              // ----------------
              // Error category 2
              // ----------------
              std::cerr << "Wrong input\n";
              return EXIT_FAILURE;
          }
      
          try
          {
              std::vector<int> v(size);
              printElement(v, 0);
          }
          catch (std::bad_alloc const&)
          {
              // ----------------
              // Error category 3
              // ----------------
              std::cerr << "Out of memory\n";
              return EXIT_FAILURE;
          }
      }
      

答案 1 :(得分:3)

不幸的是,C ++的设计理念是程序员永远不会犯任何错误。不要笑......我是认真的。

在C ++中没有&#34;运行时错误天使&#34;注意代码没有做任何愚蠢的事情,如访问边界外的数组元素或两次取消分配相同的指针。主要的假设是程序员永远不会编写那样做的代码,因此浪费CPU时间检查例如访问时的数组边界不是一个好主意。

标准库抛出异常,但是在程序员无法知道操作是否可行的情况下(比如尝试分配更多内存而不是它可用)。< / p>

在其他情况下,假设代码是正确的,不需要检查。仅作为一种设施,标准库中有一些方法使用异常机制来报告失败,例如std::vector::atdynamic::cast

而不是&#34;运行时错误天使&#34; C ++有&#34;未定义的行为守护进程&#34; (a.k.a。&#34; nasal daemons&#34;)如果您违反任何规则,而不是指向您的问题可以做任何他们想要的事情。 作为恶魔守护进程,他们喜欢应用的最危险的行为是在黑暗中保持沉默,让程序运行无论如何都能提供合理的结果......除非你在包括你父母在内的广大受众面前运行程序和你的老板(在这种情况下,他们会让屏幕看起来对你来说最尴尬)。

C ++的梦想是,在编译时移动所有有效性检查将从运行时错误中保存程序。尽管这是一个明显的错觉,但这就是语言的设计;在C ++中编程的唯一方法是避免所有错误(目标也不那么容易,因为语言非常复杂)。

为了侮辱伤害,微软决定当一个程序做得很糟糕时,即使操作系统本身也可以看到它是一个明显的错误,报告机制仍然应该基于异常抛出。这样,如果访问的内存甚至不在其地址空间内,则可以编写不会因崩溃而退出的程序。捕获甚至吞噬(!)这些错误的能力永远不应该使用,除非你定义一个强大的程序&#34;是&#34;一个难以杀死的程序&#34;。

这是您catch(...)正在做的事情......

答案 2 :(得分:1)

您应该使用std::vector及其方法at(int index)来保证数组边界检查:

#include <iostream> 
#include <vector>

int main()
{
    std::vector<int> a(100);
    try{
        int index = 101;
        a.at(index) = 1;
        std::cout << a.at(index) << std::endl;
    }
    catch (const std::out_of_range& ex) {
        std::cerr << "Out of Range error: " << ex.what() << std::endl;
    }
    return 0;
}

答案 3 :(得分:1)

您的问题的一个答案是C ++不会阻止您编写有害代码。您的代码正在写入您不了解的内存空间。当您将索引设置得非常高时,您可能会写入一个不允许您的应用程序触摸的位置,这就是它失败的原因。

在MS Visual C ++中,您可以设置不同的警告级别,其中一些警告级别会警告您不安全的代码,因此您可以在发货之前修复它/使其更安全。

您还可以尝试其他可以检查代码的工具。 (我用过:http://cppcheck.sourceforge.net/