Permalänk

C++ och minnesläckor

Skrev en tråd här för ett tag sedan om c++ och minnesallokering och nu är det så att hur mycket jag än tillämpar den så får jag ingen rätsida på det hela. Programmet som jag har gjort läcker minne som ett såll och jag har ingen aning om vad jag ska göra åt det. Något folk har rekommenderat är valgrind men det är ju linux baserat och för mig oanvändbart. Finns det någon annan metod eller finns det någon här på forumet som orkar ta sig en titt på min (troligen alldeles hemska) kod och kanske lura ut vad som är fel med den. Programmeringen i sig är ganska basic men det kan hända att koden, trots det och det faktum att jag har ansträngt mig för att göra den överskådlig, är svårläslig.

Permalänk
Medlem

Posta koden så kan vi hjälpa till

Permalänk
Medlem

Minnesläckor är generellt inte så svåra att hitta...
Kör en delete på allt som är newat... Om du använder nåt bibliotek, typ sdl eller nåt och har nån createfunktion - kör också deras destoyfunktion etc.

Gör man bara allt sånt så blir det prima.

Permalänk
Medlem

Använd MemProof

Visa signatur

www.filipsprogram.tk - lite freeware
"Delight, herregud. Talang är bara förnamnet."

Permalänk
Inaktiv

Om Valgrind inte duger så gissar jag att du använder visual studio?
I så fall finns det lite funktioner du kan använda för att hitta läckorna (http://www.codeguru.com/forum/showthread.php?t=312742). I vilket fall som helst så kan också börja använda t.ex std::auto_ptr och boost::shared_ptr.

Den bästa lösningen är dock att bättra på din kod så den inte kan antas vara hemsk

Permalänk
Glömsk
Citat:

Ursprungligen inskrivet av poppeman
Om Valgrind inte duger så gissar jag att du använder visual studio?
I så fall finns det lite funktioner du kan använda för att hitta läckorna (http://www.codeguru.com/forum/showthread.php?t=312742). I vilket fall som helst så kan också börja använda t.ex std::auto_ptr och boost::shared_ptr.

Den bästa lösningen är dock att bättra på din kod så den inte kan antas vara hemsk

Fast auto_ptr kan vara lömsk speciellt om man inte redan har bra koll på dynamisk minnesanvändning. Du kan ju exempelvis inte använda auto_ptr tillsammans med standardcontainrarna, som man kan med vanliga pekare. Vet man inte detta kan man introducera nya problem.

Jag gillar f.ö. Valgrind.

Visa signatur

...man is not free unless government is limited. There's a clear cause and effect here that is as neat and predictable as a law of physics: As government expands, liberty contracts.

Permalänk

Jaja om ni insisterar så, det här är den nedbantade versionen, och jag har varnat er den är nästan 250 rader.

#include<iostream> #include<conio.h> #include<fstream> #include<vector> #include<windows.h> #include<ctime> #include<string.h> using namespace std; class Part { public: short iXpos; short iYpos; string sWord; string sMixed; Part(vector<string> vWordList):iYpos(2) { char cTemp; short iSize = vWordList.size(); sWord = vWordList.at(rand()%iSize); sMixed = sWord; while(sWord == sMixed) { for(short i=0; i<sMixed.size(); i++) { short iSwap = rand()%sMixed.size(); cTemp = sMixed.at(i); sMixed.at(i) = sMixed.at(iSwap); sMixed.at(iSwap) = cTemp; } } iXpos = rand()%(80-sMixed.size()); } }; void fGotoXY(int x, int y)//Flyttar makören till position (x,y). { COORD c; c.X=x; c.Y=y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c); } double fGameTime() { return(clock()/CLOCKS_PER_SEC); } class Guess { public: char cTemp; string sGuess; bool bFound; string::iterator sIterator; Guess():sGuess(""), bFound(false) { sIterator=sGuess.begin(); } }; class Game { public: vector<string> vWordList; vector<Part*> vGame; vector<Part*>::iterator vIterator; Guess sGuess; short iLevel, iTaken, iMissed, iUpdateCount; int iScore; double dStart; bool bNotLost; Game(string sFile):iLevel(1), iTaken(1), iMissed(1), iScore(0), iUpdateCount(0),bNotLost(true) { ifstream readFile; readFile.open(sFile.c_str()); string sTemp; while(getline(readFile, sTemp)) vWordList.push_back(sTemp); } void addWord(vector<string> vList)//Slumpar och lägger till ett nytt ord { vGame.push_back(new Part(vWordList)); vIterator=vGame.begin(); } void checkKeyBoard()//Kontrollerar användarens tangenttryckningar. { while(kbhit()) { fGotoXY(30, 0); sGuess.cTemp = getch(); sGuess.sGuess += sGuess.cTemp; cout << sGuess.sGuess; if(sGuess.cTemp == 13) checkGuess(); else if(sGuess.cTemp == '\x8') { for(short i=0; i<2;i++) { sGuess.sIterator=sGuess.sGuess.begin()+sGuess.sGuess.size()-1; sGuess.sGuess.erase(sGuess.sIterator); } fGotoXY(30+sGuess.sGuess.size(), 0); cout << " "; } } } void checkGuess()//Kontrollerar om det inmatade matchar något ord i vektorn { for(vIterator=vGame.begin(); vIterator!=vGame.end()&& !sGuess.bFound; vIterator++) { if(sGuess.sGuess.substr(0,(*(*vIterator)).sWord.size()) == (*(*vIterator)).sWord && sGuess.sGuess.size()==(*(*vIterator)).sWord.size()+1) { removeWord(1); sGuess.bFound=true; } } sGuess.bFound=false; fGotoXY(30, 0); for(short i=0; i<sGuess.sGuess.size();++i) cout<<' '; sGuess.sGuess = ""; } void removeWord(bool bTake)//Plockar bort ett ord från vektorn { iScore +=(int)((39-(*(*vIterator)).iYpos)*(*(*vIterator)).sWord.size()); iLevel = (iScore+200)/200; if(!bTake) { iMissed++; if((double)iTaken/iMissed < 1) loseGame(); } else iTaken++; fGotoXY((*(*vIterator)).iXpos, (*(*vIterator)).iYpos); for(short i=0; i<(*(*vIterator)).sMixed.size(); i++) cout << " "; delete (*vIterator); vGame.erase(vIterator); vIterator = vGame.begin(); } void updateScreen()//Ritar om skärmen { fGotoXY(7, 0); cout << iScore; for(vIterator=vGame.begin(); vIterator!=vGame.end(); ++vIterator) { fGotoXY((*(*vIterator)).iXpos, (*(*vIterator)).iYpos); for(short n=0; n<(*(*vIterator)).sMixed.size(); n++) cout << ' '; (*(*vIterator)).iYpos++; } for(vIterator=vGame.begin(); vIterator!=vGame.end(); ++vIterator) { fGotoXY((*(*vIterator)).iXpos, (*(*vIterator)).iYpos); cout << (*(*vIterator)).sMixed; } } void runGame()//Spelets hjärta { system("cls"); fGotoXY(0,0); cout << "Score: 0\n"; for(int x = 0; x<80; x++) cout << (char)196; addWord(vWordList); updateScreen(); dStart = fGameTime(); while(bNotLost) { if(iLevel > 10) iLevel = 10; while(1) { checkKeyBoard(); if(fGameTime()-dStart>0.5) break; } if(iUpdateCount == 11-iLevel) { addWord(vWordList); iUpdateCount = 0; } for(vIterator=vGame.begin(); vIterator!=vGame.end();++vIterator) { if((*(*vIterator)).iYpos >= 39) removeWord(0); } updateScreen(); dStart = fGameTime(); ++iUpdateCount; } } void loseGame()//Visar missade ord och en highscorelista. { bNotLost=false; } }; int main()//Programmet läcker som ett såll. { srand(time(0)); Game *gIterator = new Game ("en1.txt"); (*gIterator).runGame(); delete(gIterator); }

Memproof sa ni, ska kolla upp det. Och om någon mpt förmodan orkat ned hit hoppas jag att det inte är något allt för enkelt då jag, inaktivt, spenderat halva veckan på att felsöka koden. Och förresten, klaga gärna på annat som irriterat er.

Permalänk
Medlem

Ett par spontana kommentarer:

1. Man behöver inte alltid skapa objekt med new, så main-funktionen kan lätt kortas till:

int main() { srand(time(0)); Game game ("en1.txt"); g.runGame(); }

Försök hitta nån tutorial eller bok som förklarar skillnaden mellan stack- och heap-allokerade objekt och när och hur man ska använda dem. Ju färre new du har, ju färre potentiella minnesläckor finns ;).

2. (*foo).bar() är ekvivalent med foo->bar(), kan ge mer läsbar kod.

Permalänk

Jag är väl, eller åtminstone godtyckligt, införstådd i skillnaden mellan stack och heap. Anledningen till att jag i det här fallet ville använda heapen är för att programmet egentligen ska kunna köras om och om igen från en meny som på grund av utrymmesskäl inte finns med i den koden du har framför dig. Och för det andra, jag är medveten om att (*). är ekvivalent med -> men jag själv upplever inte det som smidigare, möjligen mer lättläst, och absolut inte mer lättbegripligt då jag anser att innehållsoperatorn ger en mer intuitiv bild av vad det faktiskt gäller. Metoden bar() tillhör innehållet i adressen foo pekar på vilket jag tycker är en bättre förklaring än att foo leder till bar();så tolkar åtminstone jag fritt syntaxet.

Kan tillägga för er som vill testa programmet att som man ser i main behövs ett textdokument med ett innehåll för att programmet ska fungera enligt avsikt.

Permalänk
Medlem

Fast du behöver väl ändå inte heapen (med reservation för att jag ju inte vet hur hela programmet ser ut)? Objekt man skapar på stacken finns kvar där tills du lämnar funktionen, så skapar du stackallokerade objekt i main finns de där tills programmet avslutar och du kan skicka runt dem med referenser (eller pekare om du vill) hur mycket du vill. Det gäller för alla funktioner att stackutrymmet finns kvar tills du lämnar funktionen, oavsett hur många andra funktioner du kallar på däremellan. Om du däremot i någon funktion vill skapa ett objekt för att returnera det till koden som kallade på funktionen måste du skapa det på heapen och returnera en pekare (alternativt se till att klassen har en vettig copy-konstruktor så att du kan returnera det by-value).

Permalänk

Sant, och det använde jag mig flitigt av innan jag började programmera objektorienterat men som du sa vet du inte hur programmet ser ut; det är uppbyggt så att man vid starten har olika alternativ och den här koden är bara en del av det hela, man ska kunna köra den först en gång, sedan göra någonting annat och sedan gå tillbaka och köra den igen och av den anledningen vill jag vara helt säker på att det är ett nytt huvudobjekt jag skapar och är inte så pigg på att återanvända det gamla då det här med pekarelagring inte sitter som berget i huvudet än. Dessutom behöver jag visa att jag kan använda heapen(det här är ett skolprojekt nämligen). Förra gången grät läraren nästan när han fick se min kod, undra om han gör det den här gången.

Permalänk
Inaktiv
Citat:

Ursprungligen inskrivet av Psionicist
Fast auto_ptr kan vara lömsk speciellt om man inte redan har bra koll på dynamisk minnesanvändning.

Jo, shared_ptr är dock relativt säker (om man bortser från cykler).

Citat:

Ursprungligen inskrivet av Vankelmod
Jaja om ni insisterar så, det här är den nedbantade versionen, och jag har varnat er den är nästan 250 rader.

// massor av saker

Den enda läckan jag upptäckte var att du inte rensar vGame vid avslut. Lägg till en destruktor eller (rekommenderat) plocka bort * ur vector<Part*> vGame (samt fixa alla följdfel).

Permalänk
Medlem

Klassen Game saknar en destruktor som deletar allt i vGame.

Permalänk

Tack för den upplysningen, det hade jag helt glömt bort; nästan så man är lite förbannad på sig själv. Lade dit följande destruktor:

~Game() { for(vIterator=vGame.begin(); vIterator!=vGame.end();++vIterator) { delete (*vIterator); vGame.erase(vIterator); } }

Jag får anta att den fungerar för det stora problemet kvarstår fortfarande, om jag skulle gissa tror jag att det är lokerat i antingen addword() eller removeWord() eftersom det är de metoderna som slutar fungera vid programkörning.

Permalänk

Jag testade att kompilera din kod och jag tycker inte det verkar finnas någon minnesläcka. Enda buggarna jag hittade var:

Om man tömmer rutan på ord så kraschar programmet.
Om ett ord kommer till under kanten så kraschar programmet efter ett par sekunder.
Om det inte finns någon fil så kraschar också programmet.

Jag använde Visual till att kompilera. (Fick ta bort .h i string.h för att det skulle gå att kompilera).

Permalänk

Precis som du säger kraschar programmet när du tömmer rutan på ord, vilket inte ska hända då hela tiden nya ord ska slumpas ut så länge som man inte har missat fler ord än man har tagit. Men av någon anledning slutar programmet vid okännt tillfälle slumpa ut ord när metoden addWord() anropas.

Att programmet kraschar när ett ord når nedre kanten har jag själv inte upplevt, det är meningen att det ska plocka bort från vektorn när det når en viss y-koordinat utan att ha blivit taget. Om det var det första ordet du missade räknade programmet med att du missat fler än du tagit och avslutade. Och om det finns en fil kan programmet inte slumpa ut några ord eftersom de inte finns och då kraschar programmet.

Det är den första delen som har förbryllat mig hela vägen, jag har nu helt enkelt antagit att det är en minnesläcka eftersom jag inte kan finna någon förklaring till problemet.

Permalänk

void checkGuess()//Kontrollerar om det inmatade matchar något ord i vektorn { vIterator = vGame.begin(); while (vIterator != vGame.end() && !sGuess.bFound) { if(sGuess.sGuess.substr(0,(*(*vIterator)).sWord.size()) == (*(*vIterator)).sWord && sGuess.sGuess.size()==(*(*vIterator)).sWord.size()+1) { removeWord(1); sGuess.bFound=true; } else vIterator++; } /*for(vIterator=vGame.begin(); vIterator!=vGame.end()&& !sGuess.bFound; vIterator++) { if(sGuess.sGuess.substr(0,(*(*vIterator)).sWord.size()) == (*(*vIterator)).sWord && sGuess.sGuess.size()==(*(*vIterator)).sWord.size()+1) { removeWord(1); sGuess.bFound=true; } }*/ sGuess.bFound=false; fGotoXY(30, 0); for(short i=0; i<sGuess.sGuess.size();++i) cout<<' '; sGuess.sGuess = ""; }

Permalänk

Jag förstår verkligen inte vad du menar, det du föreslår är att byta ut for-loopen mot en while-loop, eller hur? Vad är den egentliga skillnaden? Jag kan inte riktigt se den och dessutom ger den koden ingen förbättring i programmets bettende vad jag kan se.

Permalänk

När du anropar removeWord så finns möjligheten att vIterator sätts till att peka mot vGame.end(). Vid nästa iteration av for-loopen så ökas vIterator vilket gör att programmet kraschar.

Notera att i den koden som jag postade så ökas endast vIterator om removeWord inte har blivit anropat.

Jag har testat att köra koden som jag postade och hos mig så försvann buggen med att programmet kraschar när det inte finns några ord kvar på skärmen.

Permalänk

Eh jo så är det förvisso men precis i slutet av removeWord() sätts alltid vIterator till vGame.begin() för att förebygga den sortens problem. Eller har jag missat något fundamentalt här?

Har du förresten provat att köra programmet någon längre tid, vanligtvis inom de första tio minuterna dyker ett fel upp så att inga nya ord läggs till vektorn och programmet bara står och går på tomgång.

Permalänk

Om vektorn är tom så ger vGame.begin() samma resultat som vGame.end(). Har inte provat köra någon längre tid då programmet kraschar hos mig när ett ord når botten.

Edit: Om man låter orden trilla vidare så stannar programmet efter ca en halv minut. När orden når underkanten så scrollas fönstret.

Som du sa så efter ett tag så slutar orden komma.

Permalänk
Inaktiv
Citat:

Ursprungligen inskrivet av Vankelmod
Tack för den upplysningen, det hade jag helt glömt bort; nästan så man är lite förbannad på sig själv. Lade dit följande destruktor:

Du kan ta bort vGame.erase(vIterator) (men inte delete). Containers tömmer sig själva (.clear()) i sina egna destructors.

Permalänk

Prova att köra programmet i helskärm. Det är avsett för det och jag har en funktion som fixar det men den tog jag inte med den här eftersom jag vet att den fungerar. Anledningen till att det scrollar är för att orden plockas bort automatiskt på rad 39(???). Men det här du säger att programmet kraschar när ett ord når botten är inte bra, jag vet inte varför och har aldrig upplevt det under den tid jag har utvecklat och testat programmet.

Permalänk

Den kraschar inte direkt. Om jag utökar kommandoprompten med fler rader så ser man att orden fortsätter förbi slutet. Efter ett litet tag så avslutas programmet.

Tror att default höjd på prompten är 25.

Permalänk

Som sagt är avsikten att alla missade ord ska tas bort av den här kollen:

if((*(*vIterator)).iYpos >= 39) removeWord(0);

Är du säker på att det du upplever inte är effekten av den här kollen:

if((double)iTaken/iMissed < 1) loseGame();

Är det möjligt att när ordet försvinner så ökar iMissed så att kvoten iTaken/iMissed blir mindre än 1(dvs fler missade än tagna) och att programmet avslutas därför? Om inte så skulle jag gärna vilja att fler sade hur de upplever saken. Det programmet jag har är kompilerat med devc++ 4.9.9.2 och jag tror att du nämnde att du använder visual.

Permalänk
Citat:

Ursprungligen inskrivet av Vankelmod
Som sagt är avsikten att alla missade ord ska tas bort av den här kollen:

if((*(*vIterator)).iYpos >= 39) removeWord(0);

Hos mig är prompten inställd på 25 rader som default vilket gör att din koll inte kommer att utföras fören ordet har passerat underkanten.

Citat:

Ursprungligen inskrivet av Vankelmod
Är du säker på att det du upplever inte är effekten av den här kollen:

if((double)iTaken/iMissed < 1) loseGame();

Jo, det var det som jag upplevde. Såg bara skumt ut när den började scrolla fönstret.

Citat:

Ursprungligen inskrivet av Vankelmod
Är det möjligt att när ordet försvinner så ökar iMissed så att kvoten iTaken/iMissed blir mindre än 1(dvs fler missade än tagna) och att programmet avslutas därför? Om inte så skulle jag gärna vilja att fler sade hur de upplever saken. Det programmet jag har är kompilerat med devc++ 4.9.9.2 och jag tror att du nämnde att du använder visual.

Gillar editorn i dev-c++ men debuggern är inte mycket att hurra förr.

Jag körde ditt programmet och kikade på vad variablerna innehöll för värden när det slutat komma några ord. Visade sig att iUpdateCount hade värdet 13. Detta gör ju förstås att inga fler ord kommer att matas ut.

if(iUpdateCount >= 11-iLevel) { addWord(vWordList); iUpdateCount = 0; }

Det finns även en bugg i funktionen checkKeyBoard när sGuess.sGuess är tom.

if (!sGuess.sGuess.empty()) { sGuess.sIterator=sGuess.sGuess.begin()+sGuess.sGuess.size()-1; sGuess.sGuess.erase(sGuess.sIterator); }

Permalänk

Som sagt devcs debugger är troligen det sämsta man någonsin sett men editorn är ganska skön att skriva i. Det där felet hade jag aldrig upptäckt själv, allra mest för att jag fortfarande inte kan förstå hur iUpdateCount skulle kunna bli 13 när det räknas upp ett åt gången och nollas vid tio, men jaja, nu fungerar det ju iallafall. Den där andra hade jag upptäckt själv och funderade på om jag skulle orka ändra på.

Tack för hjälpen allihopa, även om det faktiska innehållet visade sig avvika en aning från titeln. Jag hade som sagt aldrig kunnat fixa det här själv.