PhilPhil
PhilPhil

Reputation: 173

Nested QVector pointer memory handling

I've inherited a substantial Qt5 project, where the accumulative memory leakage is becoming a serious issue. (Yes, memory leakage should rarely be tolerated but with real life budget and time constraints...).

This GUI reads image data into objects of a voxel class, to be displayed graphically. The data comes either from file or buffer (if acquired live) and is stored as a nest qvector, ie:

QVector < QVector <Voxel *> > cVoxel;

When an image is read from file, cVoxel is initialized using QVector.resize(0).

cVoxel.resize(0);

When an image save to file is opened, a local Voxel pointer is created and pushed to the end of cVoxel, once for every pixel so looping over all rows and columns:

for (iRow = 0; iRow < nRows; ++iRow)
{
   for (iCol = 0; iCol < nCols; ++iCol)
   {
      Voxel *v = new Voxel;
      cVoxel[iRow].push_back(v);
      // Code for reading data into cVoxel removed here
      ...
   }
}

Courtesy of the useful comments below, I've now had some success in seeing memory usage decrease in the Windows Task Manager, by nesting destruction of the cVoxel QVector in my CTOR. Along the lines of:

for (iRow = 0; iRow < nRows; iRow++)
{
    for (iCol = 0; iCol < nCols; iCol++)
    {
        delete cVoxel[iRow][iCol];
    }
}

Ideally, a major rewrite is the best solution. But in the real world, I'll have to try and fix the bigger leaks and hope that's enough until there's enough resources available for a more ideal solution.

----EDIT ----

Apologies for the messy ad-hod commenting below, but this is certainly helping me reduce the memory leakages (complete stoppage will hopefully happen in due course..).

I've edited in-line above to (hopefully) make this post clearer, and removed my best case effort as it had no impact on the memory leak. The significant alteration above are the (2) brief paragraphs in italics.

I also need to investigate @richardcitter (sp?) polymorphism related suggestion.

--- EDIT3 ---

Removed Edit2, posted that (new) question separately here.

Also, I'm pretty confident the answer below should fix this question - I just need to figure out how to use qvector.resize() or find a workaround to it.

Upvotes: 0

Views: 437

Answers (2)

PhilPhil
PhilPhil

Reputation: 173

@SomeProgrammerDude: You got me 9/10 towards the solution.

As outlined in a connected SO post, I decided against smart pointers in the end. The above solution introduced (for me) the issue of the compiler trying to reference a deleted function. Otherwise it is correct with a couple of modifications:

QVector < QVector <Voxel *> > cVoxel;

Initialisation:

cVoxel.resize(0);

Memory assignment:

{
   for (int i = 0; i < rows; ++i)
   {
      cVoxel.push_back( QVector <Voxel *> () );
      for (int j = 0 ; j < cols ; ++j)
      {
         Voxel *v = new Voxel;
         cVoxel[i].push_back(v);
      }
   }
}

Finally DTOR releasing the memory:

   int iRow, iCol;
   for (iRow = 0; iRow < rows; iRow++)
   {
      for (iCol = 0; iCol < cols; iCol++)
      {
           delete cVoxel[iRow][iCol];
      }
   }

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409442

To solve the undefined behavior and to properly make sure that you don't need to make any extra allocations, you can preallocate the number of elements in the vectors. You already do this when you call resize(0), but instead of setting the size you really need, you make the size zero, you make the vector empty.

I would suggest something like this instead:

First use std::unique_ptr as suggested by Richard Critten:

QVector < QVector < std::unique_ptr <Voxel> > > cVoxel;

If Qt have its own unique pointer type you could use that instead.

Then when you create the you use resize to set the actual size of the vectors:

cVoxel.resize(nRows);

Then you can use plain indexes into the vector. Set the size for the inner vectors as well:

for (iRow = 0; iRow < nRows; ++iRow)
{
   cVoxel[iRow].resize(nCols);  // Resize to the number of columns

   for (iCol = 0; iCol < nCols; ++iCol)
   {
      cVoxel[iRow][iCol].reset(new Voxel);  // Create the actual Voxel object

      // Code for reading data into cVoxel here
      ...
   }
}

Since you use std::unique_ptr (or the Qt equivalent) the memory managed by the std::unique_ptr object will be automatically free'd once the object is destructed. So no more memory leaks, when the cVoxel vector goes out of scope or is otherwise destructed, so will your Voxel objects be.

Upvotes: 1

Related Questions