2014-04-02 83 views
2

此以下線程程序內的實例:C++不能修改類

void* Nibbler::moveRoutine(void* attr) 
{ 
    [...] 
     Nibbler* game = static_cast<Nibbler*>(attr); 

     while (game->_continue == true) 
     { 
     std::cout << game->_snake->_body.front()->getX() << std::endl; // display 0 
     std::cout << game->getDirection() << std::endl; // display 0 
     game->moveSnake(); 
     std::cout << game->_snake->_body.front()->getX() << std::endl; // display 0 
     std::cout << game->getDirection() << std::endl; // display 42 
     } 
    } 
    [...] 
} 

我打電話成員函數moveSnake(),這是應該修改形成我的蛇的身體細胞的位置。

void Nibbler::moveSnake() 
{ 
    [...] 
    std::cout << this->_snake->_body.front()->getX() << std::endl; // display 0 
    this->_snake->_body.front()->setX(3); 
    this->_direction = 42; 
    std::cout << this->_snake->_body.front()->getX() << std::endl; // display 3 
    [...] 
} 

雖然我的兩個座標我moveSnake內有效地修改()函數,它們不再當我回到我的常規,在那裏他們保持初始值。我不明白爲什麼會發生這種情況,因爲如果我嘗試在我的moveSnake()函數內修改我的類的任何其他值,則實例會被修改,並且會將此值保留在例程中。

了衝切類:

class Nibbler 
{ 
public : 
    [...] 
    void   moveSnake(); 
    static void* moveRoutine(void*); 

private : 
    [...] 
    int   _direction 
    Snake*  _snake; 
    IGraphLib* _lib; 
    pthread_t  _moveThread; 
... 
}; 

蛇:

class Snake 
{ 
public : 

    [...] 
    std::vector<Cell*> _body; 
}; 

最後的細胞:

class Cell 
{ 
public : 

    void setX(const int&); 
    void setY(const int&); 

    int getX() const; 
    int getY() const; 

    Cell(const int&, const int&); 
    ~Cell(); 

private : 

    int _x; 
    int _y; 
}; 

的cell.cpp代碼:

void  Cell::setX(const int& x) 
{ 
    this->_x = x; 
} 

void  Cell::setY(const int& y) 
{ 
    this->_y = y; 
} 

int  Cell::getX() const 
{ 
    return this->_x; 
} 

int  Cell::getY() const 
{ 
    return this->_y; 
} 

Cell::Cell(const int& x, const int& y) 
{ 
    this->_x = x; 
    this->_y = y; 
} 

Cell::~Cell() 
{} 
+1

在我看來,你是在一個多線程環境(或者我沒有看到'moveRoutine'使用靜態方法的觀點)。也許另一個線程在同一時間重新初始化數據。並且要在C++中創建一個線程,如果需要,最好使用'std :: thread'並最終使用'std :: bind',這將保留類結構並防止使用'void *' – Geoffroy

+0

我在這裏使用的唯一線程是調用moveRoutine的那個。此外,如果我在moveSnake()中設置了任何其他屬性,它將起作用,所以我猜測問題來自於我以這種方式在我的向量中設置Cell的事實,但我看不出爲什麼。 – Kernael

+0

你可以顯示set()和get()的代碼嗎?我沒有看到你的代碼不能工作的原因。 @Geoffrey:另一個線程怎麼會在那裏干擾?除非在Snake中有一個reset()方法或類似的東西。顯示的用於設置和獲取的測試代碼顯然是在單個線程中,所以不應該存在缺失寫入問題。 –

回答

2

表面上,你的問題(「爲什麼這個成員不應該被修改,當它應該?」)似乎是合理的。已經顯示的設計意圖已經足夠清晰,我認爲它與您所描述的內容相符。但是,你的程序的其他元素已經合謀使它不是這樣。

有一件事可能會讓你感到困擾的是Undefined Behavior。不管你信不信,即使是最有經驗的C++開發者偶爾也會遇到UB。另外,堆棧和堆損壞是導致非常難以隔離的問題的極其簡單的方法。你有幾件事情要轉向以root出來:

調試器(從這裏開始!)

  • 用一個簡單的單步調試器,您可以通過您的代碼行,並在每個檢查你的假設轉。設置一個斷點,執行直到檢查內存/變量的狀態,再次平分問題空間,迭代。

靜態分析

  • 與編譯器警告啓動和向上移動到皮棉和複雜的商業工具,靜態分析可以幫助指出「代碼味道」,可能不一定是UB,但可能是死碼或者其他地方你的代碼可能不會做你認爲它的事情。
  • 您是否忽略了您撥打電話的庫/操作系統返回的錯誤?就你而言,你似乎直接操縱內存,但這是期望與現實之間不匹配的常見原因。
  • 您是否有方便的rubber duck

動態分析

  • 工具,比如電子圍欄/淨化/ Valgrind的(MEMCHECK,helgrind)/地址,消毒,螺紋消毒劑/擋泥板可以幫助查明你寫入存儲器以外的地區什麼被分配。

如果您還沒有使用調試器,那麼這是您的第一步。如果你以前從未使用過,現在是你必須暫時停頓並學習如何的時候。如果你打算超越這個水平,你會感激你做到了。

如果您在Windows上開發,您很有可能使用Visual Studio。調試器可能很好地集成到您的IDE中。燃燒起來;動起來!

如果您正在linux/BSD/OSX上開發,您可以訪問gdbXCode,兩者對於此問題都應該足夠簡單。閱讀教程,觀看視頻,盡其所能,並讓調試器滾動。你可能會很快發現你的代碼已經修改了一個Snake的實例並打印出另一個實例的內容(或類似的令人沮喪的東西)。

如果在使用調試器時無法重現問題條件,恭喜!您已找到heisenbug。它可能表示一個race condition,僅此信息將幫助您磨練問題的根源。