此代码不会导致异常:
int *a=new int[100];
try{
a[101]=1;
}
catch(...){
//never called
}
但是,如果索引较大(a [11111111] = 1),则会发生错误。
是否有可能制定更严格的处理错误规则? (MSDN VC ++)
谢谢!
答案 0 :(得分: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
级别,错误的索引表示程序逻辑中更高抽象级别的问题。
重要的是你放弃了能够处理"所有错误都一样。将您的错误分为三个不同的类别:
错误。错误意味着您的代码错误,并且您的程序不是您认为的那样。对这些错误使用assert
和/或依靠编译器在C ++标准库组件中引入相应的检查。错误的代码应该快速崩溃,以便尽可能减少伤害。提示:MSVC有一些名为"调试版本"。 A"调试版本"是一组编译器和链接器选项,它允许进行大量额外的运行时检查,以帮助您找到代码中的错误。
错误的输入。所有程序都收到错误的输入。你绝不能假设输入是正确的。输入来自人类还是来自机器并不重要。错误的输入必须是正常程序流程的一部分。不要使用assert
或错误输入的例外。
外部资源的异常状态。这是应该使用的异常。每个程序都依赖于一种形式的外部资源,通常由操作系统提供。主要的外部资源是内存。像std::vector<int> x(100);
这样的东西通常不会失败,但理论上它可能,因为它需要记忆。启动新线程通常不会失败,但理论上,操作系统可能无法启动。这些都是特殊情况,因此异常是处理它们的好方法。
当然,这些是粗略的指导方针。在错误的输入和外部资源问题之间划出一条确切的界线尤其困难。
不过,这是一个试图总结指南的例子:
#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::at
或dynamic::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/)