2016-11-22 57 views
1

我正在尝试为std::vector创建一个容器类,以教会自己更多地了解模板,重载操作符和管理异常。std ::向量segfaulting而不是抛出异常

目前,我只是定义了基本操作。我有一个下面列出的模板类;我已将+=[]运算符重载为push_backvectorT,并分别直接访问该向量的元素。这按预期工作。

+=运算符做它应该做的事情,并试图在超出范围的元素上使用运算符[]将按预期抛出异常。

这里是原型类和实施,因为它目前为:

#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编译。

+1

为什么不简单地使用'std :: vector :: at()'而不是自己写一个无效索引的测试呢? 'vector :: at()'保证会抛出一个'out_of_range'异常。 – PaulMcKenzie

+1

“这个”例外!“cout永远不会到达,程序在达到该点之前就会出错” - 您怎么知道程序将会到达那个点?也许它去了,叫'backpack.erase'。 – immibis

+0

@PaulMcKenzie也许这是一个编码练习。 – immibis

回答

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"); 
+0

棕榈,见面。非常感谢你。 –

0

另一种方法是利用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

+0

看起来很棒!我非常感谢你指出这一点。自学的部分问题是,很容易错过这样的小事情。 :) –

1

你在代码中有一个“关闭的一个”错误,

请考虑如果数组不为空且b == backpack.size()在代码中发生了什么。

if (backpack.empty() || backpack.size() < b) 
      throw string("Out of Range"); 
return backpack[b]; 

在这种情况下,为了backpack元件的有效索引是通过0backpack.size() - 1

如果b == backpack.size(),代码不会抛出异常,并且将尝试返回backpack[backpack.size()],这会导致未定义的行为。

未定义行为的一个可能症状是“段错误”。

避免此问题的一种方法是将测试更改为backpack.size() <= b

+0

确实。这里还有一些人指出了确切的问题。我已经纠正它。我很欣赏这些反馈和帮助!有时候,这是最简单的疏忽,可能会影响程序,不是? :) –