std::vector 段错误而不是抛出异常
Posted
技术标签:
【中文标题】std::vector 段错误而不是抛出异常【英文标题】:std::vector segfaulting instead of throwing exception 【发布时间】:2016-11-22 03:02:15 【问题描述】:我正在尝试为 std::vector
创建一个容器类,以自学更多关于模板、重载运算符和管理异常的知识。
目前,我只是定义基本操作。我有一个下面列出的模板类;我已经用T
将+=
和[]
运算符重载为push_back
和vector
,并分别直接访问向量的元素。这按预期工作。
+=
操作符做了它应该做的事情,并且尝试对超出范围的元素使用[]
操作符将按预期抛出异常。
这是原型类和当前的实现:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <class T>
class Inventory
public:
void operator += (const T& b) backpack.push_back(b);
T operator [] (const unsigned& b)
if (backpack.empty() || backpack.size() < b)
throw string("Out of Range");
return backpack[b];
void operator -= (const unsigned& b)
if (backpack.empty() || backpack.size() < b)
throw string("No such element exists.");
backpack.erase(backpack.begin() + b);
private:
vector<int> backpack;
;
int main()
Inventory<int> pack;
pack += 2;
pack += 4;
try
cout << "It was " << pack[0] << endl;
cout << "It was " << pack[1] << endl;
pack -= 0;
cout << "It is now " << pack[0] << endl;
//pack -= 1; // Segfaults?
catch (string e)
cout << "Error: " << e << endl;
问题在于-=
运算符,旨在擦除右侧指示位置的元素。当我停留在向量的边界内时,这会按预期工作;但是,如果我指定要擦除的越界数字,我不会得到异常;我得到了一个 seg-fault 。我试图通过添加额外的打印命令来确定段错误发生的确切点:
void operator -= (const unsigned& b)
cout << "In Overload!\n";
if (backpack.empty() || backpack.size() < b)
cout << "Exception!\n";
throw string("No such element exists.");
backpack.erase(backpack.begin() + b);
“例外!”线永远达不到。即使我应该评估未定义的行为,程序也会在它达到那个点之前出现故障。我相信我错过了理解这个过程如何工作的关键组成部分。有没有办法我应该写这个所以它可以抛出而不是错误?
在 Linux x64 架构上使用 g++ -std=c++17 -Wall -Wextra -pedantic
编译。
【问题讨论】:
为什么不简单地使用std::vector::at()
而不是为无效索引编写自己的测试? vector::at()
保证会抛出 out_of_range
异常。
““异常!”cout 永远不会到达。程序在到达该点之前发生故障“ - 你怎么知道程序将到达那个点?也许它去叫backpack.erase
。
@PaulMcKenzie 也许这是一个编码练习。
确实如此。正如我所提到的,我正在自学 C++。因此,当我去学习一个新概念时,我倾向于尝试将新事物分解成块。首先,我专门为整数编写了这段代码。然后我把它变成了一个模板。然后我添加了错误处理和异常抛出。接下来我将使用标准异常,添加特性方法等等。我发现这让我更容易掌握新信息而不会不知所措。不过谢谢你的回复。我很感激!
一些注意事项:索引到容器中的正确数据类型是size_t
,而不是unsigned int
(尽管在某些实现中它们可能相同)。除非这是为了学习,否则您可能不应该遇到这个异常。修复程序状态没有任何意义。它遇到了意外情况,唯一安全的选择是让它终止。默认情况下,当您遇到未捕获的异常时会发生这种情况。
【参考方案1】:
您的错误检查已关闭 1。
if (backpack.empty() || backpack.size() < b)
如果std::vector
背包只包含两个值,backpack.size()
将是 2,backpack
将包含 backpack[0]
和 backpack[1]
。
不幸的是,如果索引 b
被传入为 2,此代码仍将尝试访问 backpack[2]
,从而导致未定义的行为。
其实整个if
语句可以简单改写为:
if (b >= backpack.size())
throw string("Out of Range");
【讨论】:
手掌,见脸。非常感谢。【参考方案2】:您的代码中有一个“off by one”错误。,
考虑如果数组不为空并且代码中的b == backpack.size()
会发生什么。
if (backpack.empty() || backpack.size() < b)
throw string("Out of Range");
return backpack[b];
在这种情况下,backpack
元素的有效索引是 0
到 backpack.size() - 1
。
如果b == backpack.size()
,代码不会抛出异常,并且会尝试返回backpack[backpack.size()]
,这会给出未定义的行为。
未定义行为的一个可能症状是“段错误”。
避免该问题的一种方法是将测试更改为backpack.size() <= b
。
【讨论】:
确实如此。这里的其他一些人已经指出了这个确切的问题。我已经纠正了它。我非常感谢您的反馈和帮助!有时,最简单的疏忽会导致程序出错,不是吗? :)【参考方案3】:另一种选择是利用std::vector::at(),它将在越界索引上抛出std::out_of_range
异常:
T operator [] (const unsigned& b)
try
return backpack.at(b);
catch (std::out_of_range& e)
throw string("Out of Range");
void operator -= (const unsigned& b)
try
backpack.at(b);
backpack.erase(backpack.begin() + b);
catch(std::out_of_range& e)
throw std::string("No such element exists.");
Live Example
【讨论】:
看起来很棒!我真的很感谢你指出这一点。自学的部分问题是很容易错过这样的小事情。 :)以上是关于std::vector 段错误而不是抛出异常的主要内容,如果未能解决你的问题,请参考以下文章
Laravel 5刀片在出现错误而不是抛出异常时显示空白页面
ICacheLock 上的 Apache Ignite.NET TryEnter 在网络通信错误时返回 false 而不是抛出异常