In c++ you can create new instances of a class on both the heap and stack. When overloading an operator are you able to instantiate on the stack in a way that makes sense?
You've already had a few good answers. Here's a few more points I'd like to add to them:
Point
objects. Since they are bigger than built-in types (from your code I assume they consist of two built-ins), copying them is, on most architectures, more expensive than passing them around per reference. That changes your operator to: Point Point::operator+ (Point
&
)
(Note that you have to copy the result, as there's no place it can be stored persistently so you can pass around a reference to it.) const
reference: Point Point::operator+ (
const
Point&)
. operator+()
(other than, e.g., operator+=()
) doesn't change its left argument either, you should make the compiler check that, too. For a binary operator that is a member function, the left argument is what the this
pointer points to. To make this
a constant in a member function, you have to inject a const
at the end of the member function's signature. That makes it: Point Point::operator+ (const Point&)
const
. Now your operator is what's usually called const-correct. operator+()
for your type, people will expect operator+=()
to also be present, so usually you should implement both. Since they behave quite similar, to not to be redundant you should implement one on top of the other. The easiest and most efficient (and therefor more or less canonical) way to do this is to implement +
on top of +=
. That makes operator+()
quite easy to write -- and what's even more important: basically it looks the same for every type you implement it for: Since operator+()
became quite trivial, you would probably want to inline
it. This would then be the resulting code so far:
inline Point Point::operator+ (const Point& rhs) const
{
Point result(this);
result += a;
return result;
}
These are a few basic syntactic and semantic peculiarities which (hopefully) all reading this will agree to. Now here comes a rule of thumb that I use for my code and which I find very helpful, but which probably not everyone will agree to:
The reason for the latter (take operator+=()
as an example) is rather straight-forward: In order to change it, they might need to have access to the left argument's innards. And changing class object's innards is best done through member functions.
The reasons for the former are not as simple. Among other things, Scott Meyers had an excellent article explaining that, contrary to popular belief, using non-member functions often actually increase encapsulation. But then there's also the fact that for the this
argument of member functions, some rules (implicit conversions, dynamic dispatch etc.) differ from those for the other arguments. Since you want both arguments to be treated equally, it might be surprising under some circumstances to have different rules apply to the left-hand side.
The code then looks like this:
inline Point operator+ (const Point& lhs, const Point& rhs) const
{
Point result(lhs);
result += rhs;
return result;
}
To me, this is the ultimate canonical form of it which I write down in my code without much thinking about it, no matter what type it is.
Implementing operator+=()
is left as an exercise to the reader. :)
Data and Code are orthogonal concepts. What difference does it make to have Code work on an object from the Heap as opposed to one residing on the stack? (provide you are respecting object scope in both cases)
You're right about objects on stack being destroyed once going out of scope.
But you ignore that C++ will use temporary objects are necessary. You must learn when a temporary variable will be created (and then optimized away) by the compiler for your code to work.
Note that in the following, I'm describing a very simplified "pure" viewpoint of what's happening: Compilers can and will do optimizations, and among them, will remove useless temporaries... But the behavior remains the same.
Let's start slowly: What is supposed to happen when you play with integers:
int a, b, c, d ;
// etc.
a = b + (c * d) ;
The code above could be written as:
int a, b, c, d ;
// etc.
int cd = c * d ;
int bcd = b + cd ;
a = bcd ;
When you call a function with a parameter passed "by value", the compiler will make a temporary copy of it (calling the copy constructor). And if you return from a function "by value", the compiler will, again, make a temporary copy of it.
Let's imagine an object of type T. The following code:
T foo(T t)
{
t *= 2 ;
return t ;
}
void bar()
{
T t0, t1 ;
// etc.
t1 = foor(t0) ;
}
could be written as the following inlined code:
void bar()
{
T t0, t1 ;
// etc.
T tempA(t1) // INSIDE FOO : foo(t0) ;
tempA += 2 ; // INSIDE FOO : t *= 2 ;
T tempB(tempA) // INSIDE FOO : return t ;
t1 = tempB ; // t1 = foo...
}
So, despite the fact you don't write code, calling or returning from a function will (potentially) add a lot of "invisible code", needed to pass data from one level of the stack to the next/previous.
Again, you need to remember that the C++ compiler will optimize away most temporary, so what could be seen as an innefficient process is just an idea, nothing else.
Your code will leak: You "new" an object, and don't delete it.
Despite your misgivings, the right code should be more like:
Point Point::operator+ (Point a)
{
Point c = Point(this->x+a.x,this->y+ a.y) ;
return c ;
}
Which with the following code:
void bar()
{
Point x, y, z ;
// etc.
x = y + z ;
}
Will produce the following pseudo code:
void bar()
{
Point x, y, z ;
// etc.
Point tempA = z ; // INSIDE operator + : Point::operator+ (Point a)
Point c = z ; // INSIDE operator + : Point c = Point(this->x+a.x,this->y+ a.y) ;
Point tempB = c ; // INSIDE operator + : return c ;
x = tempB ; // x = y + z ;
}
You make too much temporaries. Of course, the compiler will probably remove them, but then, no need to take sloppy habits.
You should at the very least write the code as:
inline Point Point::operator+ (const Point & a)
{
return Point(this->x+a.x,this->y+ a.y) ;
}
Using your code:
Point Point::operator+ (Point a)
{
Point result(this->x+a.x,this->y+ a.y);
return result;
}
This will work fine.
Basically it creates result localy (on the stack). But the return statement copies the result back to the calling point (just like an int). It uses the Point copy constructor to copy the value back to the call point.
int main()
{
Point a(1,2);
Point b(2,3);
Point c(a + b);
}
Here the operator + creates a local on the stack. This is copied back to the call point (The constructor for c) by the return. Then the copy constructor for c is used to copy the content into c.
But you think that seems a little costly on the copy construction. Technically yes. But the compiler is allowed to optimize away the extra copy constructions (and all modern compilers are very good at it).
Returning to your code.
Point Point::operator+ (Point a)
{
Point *c = new Point(this->x+a.x,this->y+ a.y);
return *c;
}
Don't do this. Here you have allocated dynamically but you are copying the result back to the call point (as described above using the copy constructor). So by the time control returns to the call point you have lost the pointer and can't de-allocate the memory (Thus a memory leak).
The difference between Java and C++ is that when we return pointers we use smart pointers to help the caller identify who is responsible for freeing the memory (look up pointer ownership).
If you're talking about for example operator+
, where the object returned is not either of those input, then the answer is you instantiate on the stack and return by value:
struct SomeClass {
int value;
};
SomeClass operator+(const SomeClass &lhs, const SomeClass &rhs) {
SomeClass retval;
retval.value = lhs.value + rhs.value;
return retval;
}
or
class SomeClass {
int value;
public:
SomeClass operator+(const SomeClass &rhs) const {
SomeClass retval;
retval.value = this->value + rhs.value;
return retval;
}
};
or even:
class SomeClass {
int value;
public:
SomeClass(int v) : value(v) {}
friend SomeClass operator+(const SomeClass &lhs, const SomeClass &rhs) {
return SomeClass(lhs.value + rhs.value);
}
};
The compiler then worries about where (on the stack) the return value is actually stored.
It will for example apply return-value optimizations if it can, but in principle what's happening is "as-if" the work you do constructs some value on the stack of your operator overload, and then at return this is copied to wherever it needs to be next. If the caller assigns the return value, it's copied there. If the caller passes it by value to some other function, it's copied wherever the calling convention says it needs to be in order to be that function parameter. If the caller takes a const reference, then it's copied to a temporary hidden away on the stack.
You are correct that data on the stack is unusable when the function executes. However, it is perfectly okay to return copies of data on the stack (which is what you are doing). Just make sure you don't return pointers to data on the stack.