2010-09-12 41 views
5

所以我正在製作一個帶有傳送和通常老鼠的蛇類遊戲。我有一個循環中運行這樣的:神祕的海森堡?

while(snake.alive() && miceEaten < micePerLevel) 
{ 
    displayInfo(lives, score, level, micePerLevel - miceEaten); 
    //some code 
    if(miceEaten()) 
    { 
     //update score... 
    } 
    //more stuff... 
} 

與上面的代碼的問題是比分被更新之前displayInfo被調用,所以吃了老鼠後,用戶必須等待,直到循環再次運行到看到他的分數更新。讓我感動的代碼一行到函數的底部:

while(snake.alive() && miceEaten < micePerLevel) 
{ 
    //some code 
    if(miceEaten()) 
    { 
     //update score... 
    } 
    //more stuff... 
    displayInfo(lives, score, level, micePerLevel - miceEaten); 
} 

和傳送點停止工作!當蛇到達傳送時程序崩潰。而displayInfo使用下面的代碼:

stringstream s; 
s << "LEVEL " << left << setw(12) << level << "LIVES: " << setw(12) << lives << "MICE LEFT: " << setw(12) << miceLeft 
    << "SCORE: " << setw(13) << score; 
printLine(0, s.str(), WHITEONBLUE); 

printLine只有一個color_setmvprintwrefresh()。與Teleport無關。奇怪的。

所以我去了蛇函數,其中蛇從一個瞬移獲得其下一個位置:

body.push_back(teleports[overlap(next)]->teleportFrom(dir)); //next is a Location object 

teleports[overlap(next)]->teleportFrom(dir)返回該位置的蛇是遠距傳送到。在試圖瞭解爲什麼它崩潰(可能Teleport返航一些位置離屏?),我添加上述前行下面3條線:

Location l = teleports[overlap(next)]->teleportFrom(dir); 
    mvprintw(1, 0, "(%i, %i)", l.x, l.y); 
    refresh(); 

而且問題會消失!

不僅如此,但我必須有這三條線。如果我註釋掉mvprintw(1, 0, "(%i, %i)", l.x, l.y);refresh();或兩者,則程序會在到達傳送時像以前一樣崩潰。

有關可能導致此行爲的任何想法?

更新:我試圖消除所有的警告(其中大部分是約/無符號數的符號比較警告),但只有1保持至今:

warning: reference to local variable 'other' returned 

,代碼:

Location& Location::operator = (Location other) 
{ 
    if(this == &other) 
     return other; 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

我該如何解決此警告?

+2

您在某處遇到未定義的行爲。 (很可能,您訪問的是某些界限不合適的東西,並且無法使用堆棧,周圍的數據或其他任何東西。)您需要逐步檢查代碼並檢查所有訪問。 – GManNickG 2010-09-12 23:43:38

+2

您的代碼中某處存在一些未定義的行爲。編譯您的代碼,並打開所有警告。並確保沒有警告報告(警告通常是錯誤)。如何打開警告將取決於編譯器。如果這不起作用,你需要使用一個工具來告訴你內存損壞(依賴於平臺)。 – 2010-09-12 23:44:19

+0

@Martin York - 我試過刪除所有警告,請參閱我的更新。我認爲這個警告可能是原因,但我需要一些幫助。謝謝! – wrongusername 2010-09-12 23:59:31

回答

7

構建你的賦值運算符是這樣的:
你應該總是返回* this(即使它們相等)。但他們永遠不會因爲你正在創建一個本地副本(所以這不是你的錯誤)。

Location& Location::operator = (Location const& other) 
{ 
    // Does it really matter if you assign to self? 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

標準的副本和交換對於這樣一個簡單的類似乎有點矯枉過正。

PS。您應該修復所有警告(即使它們與未簽名的不匹配一樣簡單)。如果你不解決它們,你將變得免疫於它們的效力,並且不會發現真正的問題,因爲它被警告所包圍,你忽略了。因此,修復它們(aI總是打開標誌,使編譯器將所有警告視爲錯誤,以便在出現任何警告時不會編譯代碼)。

實現賦值運算符的正確方法(或最普遍接受的好方法)。是使用複製和交換成語:

所有的
// notice the parameter is passed by value (i.e. a copy). 
// So the copy part is aromatically taken care of here. 
// So now you just need tom implement the swap() part of the idiom. 
Location& Location::operator = (Location other) 
{ 
    this->swap(other); 
    return *this; 
} 

void Location::swap(Location& other) 
{ 
    std::swap(x, other.x); 
    std::swap(y, other.y); 
} 
+0

呵呵,我的教授在課堂上寫道:)但是用你寫的,有0個警告,代碼運行良好。謝謝! – wrongusername 2010-09-13 00:21:04

+1

您不能總是將所有警告視爲錯誤。通常,您需要系統頭文件(尤其是比較晦澀的文件頭),它們在使用時總是會產生警告,儘管工作正常。在這種情況下,最好的辦法是將有問題的標頭的使用隔離到單個源文件中,以便至少其餘的代碼可以無警告。 – 2010-09-13 00:27:09

+0

@wrongusername @Martin:只有在你需要編寫三巨頭的時候,你纔會使用copy-and-swap成語。 「位置」實際上是否需要一個自定義賦值運算符? (因爲你沒有管理任何東西,所以我懷疑它!) – GManNickG 2010-09-13 00:28:02

3

您是否檢查過導致此異常的代碼?該Heisenbug現象在這裏引述:

一個常見的例子是,當沒有優化(如編譯,生成調試模式是發生在與優化編譯器編譯,但不是在同一個程序的程序中的錯誤版本)

這裏有幾個原則:

  • 競爭條件?你使用線程?
  • 某處指針溢出邊界?
  • 通過valgrind運行代碼來監視內存緩衝區任何不尋常的/無規律變化的地方

另一句名言:

一個用於heisenbug類似的行爲常見原因是執行在調試程序模式通常會在程序啓動前清除內存,並將變量強制到堆棧位置,而不是將它們保存在寄存器中。執行中的這些差異可以改變涉及界外成員訪問的錯誤的影響或關於內存初始內容的錯誤假設。另一個原因是調試器通常提供手錶或其他用戶界面,這些用戶界面會導致執行額外的代碼(如屬性訪問器),從而可能會改變程序的狀態。另一個原因是一個關於核心的fandango,一個指針超出邊界的效果。在C++中,許多heisenbugs是由未初始化的變量引起的。

確保開關斷開 - 不優化,完整的調試信息,清除任何現有的版本,重新啓動IDE,並重新編譯....

+2

valgrind +1。 (打我也是。) – 2010-09-13 00:01:42

+0

沒有線程 - 我還沒有學會使用這些。將檢查Valgrind,但我怎麼知道是否有任何不尋常的變化?什麼是指針溢出?我已經啓用了編譯器的所有警告,並沒有看到任何優化框被選中。謝謝! – wrongusername 2010-09-13 00:04:48

+0

@wrongusername:你在用什麼編譯器? – t0mm13b 2010-09-13 00:09:13

4
Location& Location::operator = (Location other) 
{ 
    if(this == &other) 
     return other; 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

這會返回一個參考。當函數返回時,other會發生什麼? (它死了,而你沒有提到任何東西。)由於這是你在問題區域處理的類,所以這可能是原因。重新安排周圍的代碼會在引用死變量「works」的某個條件下離開堆棧。

將其更改爲return *this,或者完全刪除該檢查。 (分配兩個變量沒有一個分支可能會一直運行不是增加一個分支,現代CPU的速度更快。)

(你也應該普遍採取的參數作爲參考,而不是由價值。)

+1

測試'this ==&other'將始終爲false,因爲'other'將始終是堆棧上分配的臨時文件。所以這個代碼雖然很差,但可能不是問題。要解決警告,請取出測試並儘早返回。 – 2010-09-13 00:22:25

+0

@Chris:啊,好點。 – GManNickG 2010-09-13 00:28:42

+0

所以問題在於OP的賦值操作符返回了對局部變量的引用(按值調用參數)。那是對的嗎? – Chubsdad 2010-09-13 02:50:48

1

首先,你的位置::運算符=應該是這樣的,而不是:

Location& Location::operator = (const Location &other) 
{ 
    if(this == &other) 
     return *this; 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

然而,這可能不解釋的崩潰。在大多數體系結構上,棧上的壞指針不會崩潰(假設x和y是int)。

現在,這是一個男人,而不是海森堡。你有其他人在某處破壞記憶。祝你好運。