2016-12-24 5 views
1

Итак, я создал программу, которая имитирует игру Tic-Tac-Toe и запускает ее с valgrind, и это говорит о том, что у меня есть утечка памяти. Что вызывает эту утечку и как ее исправить?Как исправить эту небольшую утечку памяти для моей программы?

Вот выход из Valgrind:

==15253== 
==15253== HEAP SUMMARY: 
==15253==  in use at exit: 72,704 bytes in 1 blocks 
==15253== total heap usage: 37 allocs, 36 frees, 76,864 bytes allocated 
==15253== 
==15253== 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1 
==15253== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==15253== by 0x4EC5B1F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.22) 
==15253== by 0x40104E9: call_init.part.0 (dl-init.c:72) 
==15253== by 0x40105FA: call_init (dl-init.c:30) 
==15253== by 0x40105FA: _dl_init (dl-init.c:120) 
==15253== by 0x4000CF9: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so) 
==15253== 
==15253== LEAK SUMMARY: 
==15253== definitely lost: 0 bytes in 0 blocks 
==15253== indirectly lost: 0 bytes in 0 blocks 
==15253==  possibly lost: 0 bytes in 0 blocks 
==15253== still reachable: 72,704 bytes in 1 blocks 
==15253==   suppressed: 0 bytes in 0 blocks 
==15253== 
==15253== For counts of detected and suppressed errors, rerun with: -v 
==15253== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 

А вот код:

main.cpp:

#include <iostream> 
#include "../include/Board.h" 

using namespace std; 

/** 
* Main function that is run. 
* @return: 0 on exit success 
*/ 
int main() { 
    Board b; 
    int r, c; 
    int moveCount = 0; 

    cout << "* * * * * Welcome to the Tic-Tac-Toe game! * * * * *" << endl; 
    cout << "Enter numbers 1, 2, or 3 when prompted for coordinates of your move." << endl; 
    cout << b.toString() << endl; 

    // Loops until there are no more possible moves. 
    while(moveCount < 9) { 
     // Prompts for coordinates to make a move. 
     do { 
      if(moveCount % 2 == 0) { 
       cout << "Player X's turn, enter the row and column of your move.\nRow #:"; 
      } 
      else { 
       cout << "Player O's turn, enter the row and column of your move.\nRow #:"; 
      } 
      cin >> r; 
      cout << "Column #:"; 
      cin >> c; 

      // Checks if the move is valid. 
      if(b.canPut((r - 1), (c - 1)) != 1) { 
       cout << "\nInvalid move, re-enter the desired coordinates.\n" << endl; 
      } 
     }while(b.canPut((r - 1), (c - 1)) != 1); 

     // Makes the move. 
     if(moveCount % 2 == 0) { 
      b.makeMove((r - 1), (c - 1), X); 
     } 
     else { 
      b.makeMove((r - 1), (c - 1), O); 
     } 

     cout << b.toString() << endl; 

     // Checks if there is a winner and breaks the loop if there is. 
     if(b.checkWinner() != 0) 
      break; 
     moveCount++; 
    } 

    // Prints the appropriate statement base on the winning status, if any. 
    if(moveCount == 9) { 
     cout << "\nGame over, stalemate." << endl; 
    } 
    else { 
     if(b.checkWinner() == X) { 
      cout << "\nPlayer X has won!!!" << endl; 
     } 
     else if(b.checkWinner() == O) { 
      cout << "\nPlayer O has won!!!" << endl; 
     } 
    } 

    return 0; 
} 

Board.h:

#ifndef BOARD_H 
#define BOARD_H 

#include <string> 
#define X 1 
#define O 5 
#define SIZE 3 

/** 
* Board class for tic-tac-toe project. 
*/ 
class Board { 
    private: 
     int **grid; 
    public: 
     Board(); 
     ~Board(); 
     int checkWinner(); 
     int canPut(int r, int c); 
     void makeMove(int r, int c, int val); 
     std::string toString(); 
}; 

#endif 

Board.cpp:

#include "../include/Board.h" 
#include <string> 

using namespace std; 

/** 
* Constructor for a Board object. 
*/ 
Board::Board() { 
    grid = new int*[SIZE]; 

    // Creates all the 1D arrays to make the 2D array. 
    for(int ctr = 0; ctr < SIZE; ctr++) { 
     grid[ctr] = new int[SIZE]; 
     for(int i = 0; i < SIZE; i++) { 
      grid[ctr][i] = 0; 
     } 
    } 
} 

/** 
* Destructor for a Board object. 
*/ 
Board::~Board() { 
    for(int ctr = 0; ctr < SIZE; ctr++) { 
     delete[] grid[ctr]; 
    } 
    delete[] grid; 
} 

/** 
* Checks if there is a winner for the current game. 
* @return: 0 if no winner, X if X player wins or O if O player wins 
*/ 
int Board::checkWinner() { 
    int sum; 

    // Checks all the rows for a winner. 
    for(int i = 0; i < SIZE; i++) { 
     sum = 0; 
     for(int ctr = 0; ctr < SIZE; ctr++) { 
      sum += grid[i][ctr]; 
     } 
     if(sum == 3) { 
      return X; 
     } 
     else if(sum == 15) { 
      return O; 
     } 
    } 

    // Checks all the columns for a winner. 
    for(int a = 0; a < SIZE; a++) { 
     sum = 0; 
     for(int b = 0; b < SIZE; b++) { 
      sum += grid[b][a]; 
     } 
     if(sum == 3) { 
      return X; 
     } 
     else if(sum == 15) { 
      return O; 
     } 
    } 

    // Checks the top-left to bottom-right diagonal for a winner. 
    sum = 0; 
    for(int i = 0; i < SIZE; i++) { 
     sum += grid[i][i]; 
    } 
    if(sum == 3) { 
     return X; 
    } 
    else if(sum == 15) { 
     return O; 
    } 

    // Checks the top-right to bottom-left diagonal for a winner. 
    sum = 0; 
    for(int r = 0, c = SIZE - 1; r < SIZE && c > 0; r++, c--) { 
     sum += grid[r][c]; 
    } 
    if(sum == 3) { 
     return X; 
    } 
    else if(sum == 15) { 
     return O; 
    } 

    // Returns zero because after checking all the possibilities, a winner has not been found. 
    return 0; 
} 

/** 
* Determines if there is an open spot on the board at the given coordinates. 
* @param r: the row to be checked 
* @param c: the column to be checked 
* @return: 1 if there is an open spot, 0 if not 
*/ 
int Board::canPut(int r, int c) { 
    if(grid[r][c] == 0) 
     return 1; 
    return 0; 
} 

/** 
* Simulates making a move for a player. 
* @param r: the row to set the value 
* @param c: the column to set the value 
* @param val: the value to be set at the given coordinates 
*/ 
void Board::makeMove(int r, int c, int val) { 
    grid[r][c] = val; 
} 

/** 
* Creates a representation of the board as a string. 
* @return: string of the board 
*/ 
string Board::toString() { 
    char a, b, c; 
    string output = "Board:\n"; 

    // Loops through every line for the 2D array. 
    for(int ctr = 0; ctr < SIZE; ctr++) { 

     // Loops through every value of the 1D array being checked. 
     for(int i = 0; i < SIZE; i++) { 
      output += ' '; 
      if(grid[ctr][i] == X) { 
       output += 'X'; 
      } 
      else if(grid[ctr][i] == O) { 
       output += 'O'; 
      } 
      else { 
       output += ' '; 
      } 
      if(i != (SIZE - 1)) { 
       output.append(" |"); 
      } 
      else { 
       output += '\n'; 
      } 
     } 

     // Pads each line with a line of '-' characters. 
     if(ctr != (SIZE - 1)) { 
      for(int i = 0; i < (SIZE * 4) - 1; i++) { 
       output += '-'; 
      } 
      output += '\n'; 
     } 
    } 
    return output; 
} 

ответ

2

Таким образом, утечка памяти не является вашей проблемой. Это утечка памяти, возникающая, когда программа выполняет инициализацию, прежде чем какой-либо из ваших кодов даже будет выполнен.

Valgrind обычно игнорирует их. У этого есть файл, который имеет список вещей, которые нужно игнорировать в различных библиотеках на разных платформах. Возможно, вы использовали параметр Valgrind, который сказал, чтобы он не игнорировал их, или, возможно, он не был правильно настроен для этой точной версии libstdC++ на этой платформе.

Как примечание стиля С ++, я скажу, что мне не очень нравится, что вы использовали #define в Board.h. Вы должны объявлять вещи const.

+0

Благодарим за информацию. Почему вы говорите, что я не должен использовать '# define' в Board.h? Я новичок в C++, поэтому я не привык к общим соглашениям языка. –

+0

@ AnthonyPalumbo - Препроцессор ... ну, считается уродливым. Используйте его для '# include' и (если абсолютно необходимо) условной компиляции (' # if' и '# ifdef' и т. Д.). Использование '# define' является уродливым, потому что оно полностью нарушает правила области и правила типа и различные другие вещи. C++ имеет встроенные функции. И вы можете сказать такие вещи, как 'const int foo = 5;' вместо '#define foo 5' и' foo' теперь имеет тип и обрабатывается как правильный идентификатор с областью. Почему препроцессор следует избегать, на самом деле является довольно сложным. – Omnifarious

2

Ваш код выглядит хорошо, но я бы предложил не использовать указатель указателя на сетку, который немного сложно обрабатывать. Вместо int **grid, есть int grid[SIZE][SIZE]. Тогда вам не нужны новые и удаленные звонки - нет утечки памяти! Это работает, если размер SIZE не очень большой, и вы пытаетесь распределить плату на стеке. Если РАЗМЕР большой, просто распределите Совет на кучу на b = new Board(). (Не забудьте удалить его!)

Смежные вопросы