Где утечки памяти? 2d класс массива

Я представил эту программу для класса, и в сообщении об ошибке говорится, что есть несколько утечек памяти, но я не могу их найти (я даже спросил другого профессора)

Вот сообщение об ошибке:

==27796== HEAP SUMMARY:
==27796==     in use at exit: 160 bytes in 2 blocks
==27796==   total heap usage: 192 allocs, 190 frees, 21,989 bytes allocated
==27796== 
==27796== 160 bytes in 2 blocks are definitely lost in loss record 1 of 1
==27796==    at 0x402ADFC: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==27796==    by 0x804D5C2: SweeperGrid::SweeperGrid(SweeperGrid const&) (in /tmp/grading20151030-8173-zfd6af-0/sweepertest)
==27796==    by 0x804BF57: main (sweepertest.cpp:357)

И ниже приведен любой код, где я использую новый или удаляю:

// Конструктор явного значения

 SweeperGrid::SweeperGrid(const int initialRows, const int initialCols, const int density){
    if ((initialRows<5 || initialCols<5) || (density<25 || density>75)) {
        throw out_of_range("Grid not large enough (number of rows or columns cannot be fewer than 5) or density is too low or high (must be between 25% and 75%)");
    }

numRows = initialRows;
numColumns = initialCols;
numBombs = 0;

grid = new SweeperCell*[numRows];
for(int i=0; i <numRows; i++){
    grid[i] = new SweeperCell[numColumns];
}

srand(time(0));
for(int i=0; i<numRows; i++){
    for (int j=0; j<numColumns; j++){
        if(rand()%100+1<density){
            PlaceBomb(i, j);
        }
    }
}
}

// Конструктор копирования

SweeperGrid::SweeperGrid(SweeperGrid const & source){
    numRows=source.GetRows();
    numColumns=source.GetColumns();
    numBombs=source.GetBombs();

grid = new SweeperCell * [numRows];
for(int i=0; i < numRows; i++){
    grid[i] = new SweeperCell[numColumns];
}

for(int i=0; i<numRows; i++){
    for (int j=0; j<numColumns; j++){
        grid[i][j] = source.At(i, j);
    }
}
}

// Деструктор

SweeperGrid::~SweeperGrid(){
for(int i=0; i<numRows; i++){
        delete [] grid[i];
}
delete [] grid;
}

// Функция: Перегруженный оператор присваивания

void SweeperGrid::operator= (SweeperGrid const & source){
    numRows=source.GetRows();
    numColumns=source.GetColumns();
    numBombs=source.GetBombs();

for(int i=0; i<numRows; i++){
    delete [] grid[i];
}
delete [] grid;

grid = new SweeperCell * [numRows];
for(int i=0; i < numRows; i++){
    grid[i] = new SweeperCell[numColumns];
}

for(int i=0; i<numRows; i++){
    for (int j=0; j<numColumns; j++){
        grid[i][j] = source.At(i, j);
    }
}
}

person AOlson    schedule 30.10.2015    source источник
comment
Определение класса тоже, пожалуйста.   -  person deviantfan    schedule 31.10.2015
comment
Чувак, просто используй векторы, и тогда у тебя не будет утечки памяти.   -  person Neil Kirk    schedule 31.10.2015
comment
В вашем операторе присваивания вы удаляете количество строк, найденных в назначенной сетке, а не в предыдущей сетке.   -  person jaggedSpire    schedule 31.10.2015


Ответы (1)


Ваша проблема вызвана удалением другого количества строк, чем вы выделили:

void SweeperGrid::operator= (SweeperGrid const & source){
    numRows=source.GetRows(); // numRows becomes the assigned number of rows here.
                              // This is a problem, because now you don't remember how many
                              // rows you need to delete!
    numColumns=source.GetColumns();
    numBombs=source.GetBombs();

    for(int i=0; i<numRows; i++){ // you delete rows here, but the number of rows found in
                                  // the NEW grid, not the old one.
        delete [] grid[i];
    }
    delete [] grid;

...
}

Простое исправление:

void SweeperGrid::operator= (SweeperGrid const & source){

    for(int i=0; i<numRows; i++){ // you delete rows here, while you still remember 
                                  // how many you should delete
        delete [] grid[i];
    }
    delete [] grid;
    numRows=source.GetRows(); // numRows becomes the assigned number of rows here.
                              // This is not a problem, because now you don't need to
                              // remember how many rows you need to delete.
    numColumns=source.GetColumns();
    numBombs=source.GetBombs();
...
}

Именно из-за такого рода проблем ручное использование delete и new является ужасной идеей, более того, это не безопасно для исключений (что произойдет, если в конструкторе не удастся выделить одну из строк после того, как вы уже выделены другие вещи? Вы потеряете каждый кусок памяти, который вы ранее выделили в конструкторе, и вы никогда не увидите его, пока не закроете программу.)

Гораздо более простое решение — применить R esource Aacquisition Is Initialization (сокращенно RAII) и использовать контейнер, который контролирует ресурсы выделяя их при построении и освобождая при уничтожении - вектор сделает это за вас, как и такие изящные вещи, как умные указатели. Это снимает с вас ответственность за обработку каждого. не замужем. распределение. если. даже. а. не замужем. один. терпит неудачу, потому что с RAII, в случае сбоя, уже выделенные ресурсы очищают себя.

В качестве примера, вот как мог бы выглядеть оператор присваивания, если бы я использовал вектор векторов для grid, а не динамический массив в стиле c для группы динамических массивов в стиле c:

void SweeperGrid::operator= (SweeperGrid const & source){
    numRows=source.GetRows(); 
    numColumns=source.GetColumns();
    numBombs=source.GetBombs();

    grid = source.grid;
}
person jaggedSpire    schedule 30.10.2015
comment
Я понимаю, что это не лучший способ сделать это, но именно так профессор попросил нас завершить проект. Спасибо за помощь! - person AOlson; 31.10.2015