Mischa Schirmer
Mischa Schirmer

Reputation: 31

C++: passing a pointer to a class in a function results in data copy?

In my Qt5 application I have a class 'Image' containing a 1D vector with the pixel values, and a member function which subtracts a reference image, likes this:

class Image {
public:

   QVector<float> pixels;

   void subtract(Image *refImage) {
      long i = 0;
      for (auto &pix : pixels) {
          pix -= refImage->pixels[i];
          ++i;
      }
   }
}

I also have a Class 'Data', containing a pointer to the reference image:

class Data {
public:
   Image* refImage;
}

Finally, in my main function I subtract the reference image from the image like this:

int main() {
   ...
   Image *image = new Image(args);
   Data *referenceData = new Data(args);

   image->subtract(referenceData->refImage);
   ...
}

Since I'm passing a pointer to subtract() I do not expect a copy of refImage to be made. However, what I observe, is that for every call to subtract() the memory footprint of my application increases by roughly the size of 'refImage'. I don't understand this. I thought by passing a pointer to a class to a function, I'm avoiding copies being made?

Upvotes: 0

Views: 129

Answers (1)

JaMiT
JaMiT

Reputation: 17007

You've probably encountered one of the ways in which Qt's containers differ from those of the standard library. Specifically, Qt uses implicit sharing and copy-on-write. That means that when a copy of a container is made, both the copy and the original point to the same data. It is only when an attempt is made to change the contents of one of the containers that a deep copy is made. (While your trimmed example does not show a copy being made, it is rather likely that copying is going on somewhere in your real code.)

Let's apply this to your example. When you access the data of the reference image, you have a non-const QVector, and you access the data via operator[]. This returns a non-constant reference to an element of the vector. That means you can write to it. It doesn't matter whether or not you actually write to it. The object has no way of knowing what you will do with the reference, so it must prepare for a potential write. Since the data (presumably) is shared, a deep copy is triggered. The data in refImage->pixels is copied to a new chunk of memory that you can overwrite without affecting whatever copy happens to be out there. Hence, more memory is consumed.

If you want to accomplish your task without triggering a deep copy, change refImage->pixels[i] to refImage->pixels.at(i) or perhaps refImage->pixels.value(i) since you don't check how large the reference vector is. Alternatively, declare refImage to be Image const * so that the the version of operator[] used by a const QVector is used; this version returns a constant reference, which will not trigger a deep copy.

Adapted from my answer to a similar question.

Upvotes: 1

Related Questions