简体   繁体   中英

Where are the memory leaks? 2d array class

I submitted this program for a class and the error message says that there are a few memory leaks, but I cannot find them (I even asked another professor)

Here is the error message:

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

And the following is any code where I use new or delete:

// Explicit-value Constructor

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

// Copy Constructor

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

// Destructor

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

// Function: Overloaded assignment operator

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

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 R esource A cquisition I s I nitialization 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM