1

I'm fairly new to programming in C++ and I was wondering something:

whenever I see operator overloading in C++ it's done like this:

#ifndef STONE_H
#define STONE_H

class Stone {
    private:
    int weight;

    public:
    .......

    Stone operator+(const Stone& s) {
        Stone stone;
        stone.weight = this->weight + s.weight;
        return stone;
    }

    .......
}


#endif

But when the "+" operator is called, it creates an object "stone", and returns a copy of this. This can't be good for performance when dealing with huge objects?

Wouldn't it be better to use dynamic memory for this as in the example below:

Stone * operator+(const Stone& s) {
    Stone * stone = new Stone;
    stone->weight = this->weight + s.weight;
    return stone;
}

Or am I seeing this wrong?

Thanks in advance

Sam Hendrickx
  • 93
  • 1
  • 13
  • 2
    see this: http://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization – NathanOliver May 29 '15 at 19:03
  • 3
    Who would release the objects? And here `a+b+c`? – StoryTeller - Unslander Monica May 29 '15 at 19:03
  • 1
    Always pair new with delete (having an binary operator you likely run into a memory leak). The first approach is just fine (Have a look at return value optimizations) –  May 29 '15 at 19:05
  • 1
    What you are missing is that all serious compilers implements NRVO which means they can often avoid copying the returned object in this case. Instead, they create the return value in place. Search for RVO and NRVO for more information on how that works and how it applies to this kind of operator overloading – Frederic Lachasse May 29 '15 at 19:17

2 Answers2

2

Trying to reason things out is not particularly accurate way of estimating performance: you need to actually write a program to measure whether or not one implementation is better than another.

There are a number of ways in which no copy will happen at all; the named return value optimization (NRVO) and move assignment are relevant ideas here.

Even if you decide that you want to do something like your suggestion, you shouldn't implement it as you have, since it has the wrong semantics: you've had operator+ return a pointer-to-thing rather than a thing. Also, working with pointers (especially bare pointers) is risky, since it gives you a lot more opportunities to make mistakes.

If you want to implement something along those lines, you need to wrap the pointer semantics in a class that provides value semantics.

0

Turns out on the current standard, this is a bit different:

#include <iostream>

class big {
  int* v; // Demonstration purposes. A smart pointer or a standard container would be better.
  public:
  big& operator+=(big& o) {
    for (int i=0;i<1000;++i) {
      v[i] += o.v[i];
    }
    return *this;
  }

  big(int val = 0)
      :    v{new int[1000]} // We're using RAII to prevent resource leaking.
  {
    std::cout << "a construction.\n";
    for (int i=0;i<1000;++i) {
      v[i] = val;
    }
  }

  // Copy constructor
  big(big& o) 
      :    v{new int[1000]}
  {
    std::cout << "a copy construction.\n";
    for (int i=0;i<1000;++i) {
      v[i] = o.v[i];
    }
  }

  // Move assignment
  big& operator=(big&& o) {
    std::cout << "a move assignment.\n";
    if (v) delete[] v;
    v = o.v;
    o.v = nullptr;
  }

  // Move constructor
  big (big&& o) {
    std::cout << "a move construction.\n";
    v = o.v;
    o.v = nullptr;
  }

  ~big() {
    if (v) delete[] v;
  }
};

// a will be move-constructed if using a temporary, or copy-contructed if not.
// The result will always be passed by a cheap move
big operator+(big a, big& b) {
  return std::move(a += b);
}

int main() {
  big a{1};
  big b{2};
  big c{3};

  big d = a+b+c;
}

Output: (with comments added)

a construction. // Constructed a
a construction. // Constructed b
a construction. // Constructed c
a copy construction. // a+b <- a copied into parameter "a" of operator+. b is passed by reference.
a move construction. // The temporary from the operation above, moved into parameter "a" of operator+. c is passed by reference.
a move construction. // d is move constructed from the temporary generated by a+b+c.
Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55