Я отправил эту программу для класса, и в сообщении об ошибке говорится, что есть несколько утечек памяти, но я не могу их найти (я даже спросил другого профессора)
Вот сообщение об ошибке:
==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);
}
}
}
Ваша проблема вызвана удалением количества строк, отличного от выделенного вами:
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
это ужасный идея — более того, это не исключительное исключение (что произойдет, если в конструкторе выделение одной из строк завершится неудачно после того, как вы уже выделили другие вещи? быть увиденным, пока вы не закроете программу.)
Гораздо более простым решением этого является применение рesource cquisition яs яnitialization идиома (RAII для краткости) и использование контейнера, который контролирует ресурсы, выделяя их на строительство и освобождая на уничтожение — вектор сделает это за вас, и поэтому будут такие изящные вещи умный указатели. Это снимает с вас ответственность за обработку каждого. не замужем. распределение. если. четное. а. не замужем. один. терпит неудачу, потому что с RAII, если один терпит неудачу, ресурсы уже распределены чистыми самих себя вверх.
Например, вот как мог бы выглядеть оператор присваивания, если бы я использовал вектор векторов для grid
, а не динамический массив в стиле c для набора динамических массивов в стиле c:
void SweeperGrid::operator= (SweeperGrid const & source){
numRows=source.GetRows();
numColumns=source.GetColumns();
numBombs=source.GetBombs();
grid = source.grid;
}
Других решений пока нет …