c++如何设置更严格的异常(错误)规则
c++ How to set more strict exception(error) rules?
此代码不会导致异常:
int *a=new int[100];
try{
a[101]=1;
}
catch(...){
//never called
}
但是,如果索引较大(a[11111111]=1),则会出现错误。
是否可以制定更严格的错误处理规则?(MSDN VC++)
谢谢!
不幸的是,C++的设计理念是程序员永远不会出错。别笑。。。我是认真的。
在C++中,没有"运行时错误天使"可以确保代码不会做任何愚蠢的事情,比如访问边界外的数组元素或两次释放同一指针。主要的假设是程序员永远不会编写这样的代码,因此浪费CPU时间检查访问上的数组边界不是一个好主意。
这些是标准库抛出的异常,但在程序员不可能知道操作是否可行的情况下(比如试图分配比可用内存更多的内存)。
在其他情况下,假设代码是正确的,不需要检查。然而,作为一种简单的工具,标准库中有一些方法使用异常机制来报告故障,如std::vector::at
或dynamic::cast
。
C++没有"运行时错误天使",而是有"未定义的行为守护程序"(也称为"鼻守护程序"),如果你违反了任何规则,它们可以做任何事情,而不是指向你的问题。作为恶性守护进程,他们喜欢应用的最危险的行为是在黑暗中保持沉默,让程序运行,以提供合理的结果。。。除非你在包括你父母和老板在内的广大观众面前运行这个程序(在这种情况下,他们会让屏幕看起来最让你尴尬的)。
C++的梦想是,在编译时移动所有有效性检查将使程序免于运行时错误。尽管这是一种明显的错觉,但这正是该语言的设计初衷;用C++编程的唯一方法是避免所有错误(目标不那么容易,因为语言非常复杂)。
雪上加霜的是,微软决定,当一个程序做得如此糟糕,甚至操作系统本身都能看到这是一个明显的错误时,报告机制仍然应该基于异常抛出。这允许编写程序,如果程序访问的内存甚至不在其地址空间内,则程序不会因崩溃而退出。捕捉甚至吞下(!)这些错误的能力是永远不应该使用的,除非你对"健壮程序"的定义是"难以杀死的程序"。
这就是你的catch(...)
正在做的。。。
您似乎对异常有两个误解:
- C++为所有错误抛出异常,比如Java
- C++应该由程序员编写,这样所有错误都会引发异常
两者确实都是误解。在C++中,标准库组件或语言本身很少抛出异常,只有在问题很少但超出您控制范围的情况下,或者没有其他简单的方法报告错误的情况下才应该抛出异常。
前者的一个例子是分配的内存超过了系统所能提供的内存。对于无法防止的外部资源,这是一个相对罕见的问题。因此,如果内存不足,new
确实会抛出std::bad_alloc
异常。
后者的例子是构造函数和带有引用的dynamic_cast
。
访问动态分配数组中的非法索引是而不是异常的良好用例,因为您可以轻松防止该错误。如果有什么不同的话,它应该是一个断言,因为如果你访问了一个非法的索引,那么你就有了一个编程错误,即bug无法在运行时修复Bug
处理此类低级错误的常用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有一种叫做"调试版本"的东西。"调试版本"是一组编译器和链接器选项,它允许进行大量额外的运行时检查,以帮助您查找代码中的错误。 -
输入错误所有程序都收到错误的输入。您决不能认为输入是正确的。输入来自人类还是来自机器并不重要。错误的输入必须是正常程序流的一部分。不要将
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 inputn";
return EXIT_FAILURE;
}
try
{
std::vector<int> v(size);
printElement(v, 0);
}
catch (std::bad_alloc const&)
{
// ----------------
// Error category 3
// ----------------
std::cerr << "Out of memoryn";
return EXIT_FAILURE;
}
}
您应该使用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;
}
您的问题的一个答案是C++不会阻止您编写有害代码。您的代码正在写入一个您一无所知的内存空间。当您将索引设置得很高时,您可能会写入应用程序不允许接触的位置,这就是它失败的原因。
在MS Visual C++中,您可以设置不同的警告级别,其中一些警告级别会警告您不安全的代码,因此您可以在发货前修复它/使其更安全。
您也可以尝试其他工具来检查您的代码。(我使用过:http://cppcheck.sourceforge.net/)
- VisualC++ 2010 有没有办法找出有关未处理异常错误的更多详细信息
- 打开CV异常错误,尽管我的代码是正确的
- 异常错误C++ //Visual Studio
- 提升序列化异常错误
- 返回内存异常错误的矢量
- 使用用户定义的函数查找完美数时出现浮点异常错误
- cv::内存位置出现异常错误
- OpenCV未经处理的异常错误
- 分数简化算法,浮点异常错误
- 为什么我会收到浮点异常错误
- 文本框为空时,C++Windows窗体应用程序出现未处理的异常错误
- 文本框为空时C++ Windows 窗体应用程序未处理的异常错误
- 函数传递映射时出现异常错误
- 在MS Visual Studio 2010上使用带有Cuda互操作的OpenGL缓冲区时出现异常错误
- 未处理的异常错误
- C++:关闭控制台应用后出现异常错误
- 写入对象时运行时引发异常错误
- C++ C# 包装器空异常错误
- Coin3D(Open Inventor)中出现未处理的异常错误
- Stack Pop()函数出现未处理的异常错误