2016-11-22 21 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

確實。這裏還有一些人指出了確切的問題。我已經糾正它。我很欣賞這些反饋和幫助!有時候,這是最簡單的疏忽,可能會影響程序,不是? :) –