内存泄漏在哪里? 2d数组类

时间:2015-10-30 20:59:18

标签: c++ memory-management memory-leaks heap-memory minesweeper

我为一个类提交了这个程序,错误消息说有一些内存泄漏,但我找不到它们(我甚至问过另一位教授)

以下是错误消息:

==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)

以下是我使用new或delete的任何代码:

//显式值构造函数

 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);
    }
}
}

1 个答案:

答案 0 :(得分:1)

Your problem is caused by deleting a different number of rows than you allocated:

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;

...
}

A simple fix:

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();
...
}

This sort of issue is exactly why manually using delete and new is a terrible idea--moreover, it's not exception safe (What would happen if in the constructor, allocating one of the rows failed after you'd already allocated other stuff? You'd lose every piece of memory you had previously allocated in the constructor, never to be seen until you shut down the program.)

The far easier solution to this is to apply the Resource Acquisition Is Initialization idiom (RAII for short) and use a container that controls the resources by allocating them on construction and deallocating on destruction--a vector will do this for you, and so will nifty things like smart pointers. This removes responsibility from you for handling every. single. allocation. if. even. a. single. one. fails, because with RAII, if one fails, the resources already allocated clean themselves up.

As an example, here's what the assignment operator might look like if I were using a vector of vectors for grid, and not a c-style dynamic array to a bunch of c-style dynamic arrays:

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

    grid = source.grid;
}