2016-10-09 49 views
3

我有一個Token類結構如下:段錯誤:: vector的

class Token { 
    public: 
     void* value; 
     token_type type; // enum to know which type of data is it storing inside value 
     unsigned int line; 

     Token() = default; 

     Token(void* new_value, token_type new_type): 
      value(new_value), type(new_type) 
      {} 

     ~Token() { 
      //free the memory occupied by the object pointed to by value based on it's type 
      //this also handles the case of the Token being an instantiation of Statement 
     } 
}; 

然後是類聲明一個聲明,內部令牌知道它持有一個聲明,因爲有一個特定的token_type類型。內部令牌做載體的清理工作在析構函數,因此必須有一個指向其value屬性是矢量,但我們也有在聲明該指針的拷貝,所以我們不需要從做演員void*std::vector<Token>*每次;

現在,我試圖做的是這樣的:

std::string* value = new std::string("Sample text"); 
Token* to_be_pushed = new Token((void*) value, string); //the object pointed to by value will be deleted in this Token's destructor 

Statement* new_statement = new Statement; 

new_statement->list->push_back(to_be_pushed); 

delete new_statement; //Token destructor gets called; It knows it's a statement, 
//so it knows value is pointing to a std::vector<Token*> object, and it deletes each pointer in that vector and then the vector itself 

的問題,但是,我在哪裏我在new_statement->list末推to_be_pushed線得到一個分段錯誤。

我試過把這條線分成兩部分,我知道問題出在我打電話時list->push_back,而不是訪問new_statement->list

這是我從GDB得到了回溯:

#0 0xb6e51410 in memmove() 
from /system/lib/libc.so 
#1 0x2a0066f8 in std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<Token*> 
(__first=0x2a0198d0, __last=0x0, 
__result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:378 
#2 0x2a006640 in std::__copy_move_a<true, Token**, Token**> (__first=0x2a0198d0, __last=0x0, 
__result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:395 
#3 0x2a007070 in std::__copy_move_a2<true, Token**, Token**> (__first=0x2a0198d0, __last=0x0, 
__result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:432 
#4 0x2a007010 in std::copy<std::move_iterator<Token**>, Token**> (__first=..., __last=..., 
__result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_algobase.h:464 
#5 0x2a006fb0 in std::__uninitialized_copy<true>::__uninit_copy<std::move_iterator<Token**>, Token**> (__first=..., __last=..., 
__result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:93 
#6 0x2a006f70 in std::uninitialized_copy<std::move_iterator<Token**>, Token**> (__first=..., 
__last=..., __result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:123 
#7 0x2a006eec in std::__uninitialized_copy_a<std::move_iterator<Token**>, Token**, Token*> (
__first=..., __last=..., 
__result=0x2a019908) 
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:279 
#8 0x2a006dc0 in std::__uninitialized_move_if_noexcept_a<Token**, Token**, std::allocator<Token*> > (__first=0x2a0198d0, __last=0x0, 
__result=0x2a019908, __alloc=...) 
at /data/data/com.termux/files/usr/include/bits/stl_uninitialized.h:300 
#9 0x2a007264 in std::vector<Token*, std::allocator<Token*> >::_M_emplace_back_aux<Token* const&> (this=0x2a019908, 
[email protected]: 0x2a0198d0) 
at /data/data/com.termux/files/usr/include/bits/vector.tcc:457 
#10 0x2a005b70 in std::vector<Token*, std::allocator<Token*> >::push_back (this=0x2a019908, 
[email protected]: 0x2a0198d0) 
at /data/data/com.termux/files/usr/include/bits/stl_vector.h:1049 

這究竟是爲什麼?我究竟做錯了什麼?這是我發佈的錯誤代碼嗎?

+0

'Token((void *)list,statement);'你似乎認爲調用基類的構造函數。它不會:基類使用默認的構造函數進行初始化。相反,它會創建一個臨時未命名的「Token」實例,該實例會立即銷燬。換句話說,這是一個無操作。這進一步加劇了'Token()'默認構造函數將其成員未初始化,包含隨機垃圾的事實。 –

+0

@Igor Tandetnik我怎麼才能實現正確的行爲,即在令牌的'value'和Statement的'list'中存儲與新的'std :: vector '對象相同的指針?然而,指針已經以正確的方式存儲在'list'中,所以我的錯誤與此無關。感謝您指出。 – user6245072

+0

您還沒有顯示'〜Token()'析構函數,但可能會嘗試訪問其成員變量,這些變量也是未初始化的並且包含隨機垃圾;於是你的程序展現出未定義的行爲。我很確定,我指出的問題實際上與您觀察到的行爲直接相關。 –

回答

4
Token((void*) list, statement); 

您期待這個調用超類的構造函數。這不會調用構造函數。所有這些都構造了一個臨時對象,然後立即被銷燬。調用父類的構造函數的唯一方法是在子類的初始化部分:

Statement() : Token(...) 

不過你的情況,你需要初始化子類,即其list成員,調用構造函數的超類之前。這不容易完成。雖然有這方面的解決方法,但這實際上是這個類層次結構中基本設計缺陷的一個症狀。

你有兩個選擇:

  1. 重新實現你的類層次結構。你的課程設計的方式從根本上是錯誤的。正確設計的C++代碼應該永遠不需要像鑄造到void *這樣的技巧。

  2. 手動初始化Token,在Statement的構造函數體中。使用Token的默認構造函數,然後修復它在Statement的構造體。

但是,即使你嘗試#2的方法,你可能會發現後面的道路,特別是其他的問題:你的Statementviolates the Rule Of Three類。這幾乎可以肯定會導致很多難以追蹤的錯誤。

這裏的正確答案是退後一步,並完全重新設計您的班級層次。並且爲了正確使用C++庫容器而擺脫new分配也是一個優點。

有很多方法可以重新設計這個類的層次結構,沒有額外的信息,就不可能建議正確的類設計。

+0

這解決了它。感謝您的回答。我只是一個適當的OOP(我來自Lua)的初學者,所以髒代碼是正常的,我希望。你能告訴我一些關於如何擺脫'void *'的鏈接嗎?他們一直告訴我這是錯誤的,但我無法在網上找到更好的方式來完成'void *'對我完成的任務:將指針存儲到未知類型的對象。 – user6245072

+0

「內部令牌在其析構函數中完成向量的清理」這只是其中一個問題。 'Statement'構造它,所以'Statement'應該銷燬它。這是正確的課堂設計。此外,它沒有什麼「未知」。它的類型總是一樣的。 –

+0

如果我考慮一下,Statement不需要是Token的子類。然而是的,'value'的類型事先是未知的,因爲它可能存儲一個'std :: string',因此具有一個'type'屬性包含'token_type :: string',或者它可以包含一個雙引號'token_type ::編號「等。 – user6245072