C++ operator woes

Started by
22 comments, last by Lemon_ 17 years, 6 months ago
I'm having a few problems with the way operators work in c++. Created a Vector3D class which has a few basic methods, two of which are: Vector3D& operator+=(const Vector3D& v) { x+=v.x; y+=v.y; z+=v.z; return *this; } friend Vector3D operator+(const Vector3D& v1, const Vector3D& v2) { return Vector3D(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z); } Unfortunately this allows me to do this: Vector3D test = v1 + v2 + v3; but not this: Vector3D test = (v1 + v2).Normalize(); I'm getting rather confused with the way returning references works in C++. Firstly, why is the value *this a refrence and not a pointer? I thought that returning a reference in my second method would allow me to use the returned value as a left-associative variable, but I am unable to get this code to compile: friend Vector3D& operator+(const Vector3D& v1, const Vector3D& v2) { return new Vector3D(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z); } Any explanation would be much appreciated. Thanks in advance Simon
Advertisement
From what I understand here, you seem to be confusing pointers and references. Using & after a type name will declare a reference, while using a * after a type name will declare a pointer. When you use the new operator you get a pointer returned and can thus not assign that (or return it) where the compiler expects a reference.

When you use * before a variable you are dereferencing it. That means that if Type* pX = new Type();
you can use a reference to the same object by doing Type& x = *pX;

You can not do Type& x = new Type(); (like you are attempting in the last function).

The problems you are having with Vector3D test = (v1 + v2).Normalize();
I can't see the answer to immediately without seeing the compile error. I made small test with the similar code and it compiled and ran fine.
inline Vector3D operator + (const Vector3D& v1, const Vector3D& v2)
{
return Vector3D(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z);
}

Try that.
To be honest, it would make more sense to make normalize a free-standing function. Just don't forget to pass the vector by constant reference, so it can be used on temporary values (such as sums) as well.

Vector3D Normalize(const Vector3D & arg) {  return arg / Length(arg);}
If it helps, think of a reference as "an implicitly dereferenced pointer". Meaning, it'll take up the storage of a pointer, but you'll use it as though it wasn't (main difference being the use of . to access members instead of * and ->). Its basically syntactic sugar to allow things like operator+= to work for user types the same way they work for builtin types.

So to answer your first question, *this is a Vector3D, and conceptually a Vector3D& works also a Vector3D, except that instead of copying the Vector3D and returning it (which is what happens when you leave off &) internally on the stack you just return a pointer.

Your friend function doesn't compile for this same reason. new gives you a pointer to a Vector3D but a Vector3D& is acting like the dereferenced object. So the syntax your friend function would be: return *(new Vector3D...). Obviously you don't want that because it'll leak the memory.

As for normalize... it has to do with what the compiler is/isnot willing to let you do with the temporary created by operators. STL strings have a similar problem to your vectors. This should work: Vector3D(v1 + v2).Normalize();

Truthfully, if it were me, instead of:
Vector3D test = (v1 + v2).Normalize();

I would do this, to get rid of all the temporaries:
Vector3D test(v1); test += v2; test.Normalize();

A slightly different idiom, but you get used to it.
Quote:Original post by ToohrVyk
To be honest, it would make more sense to make normalize a free-standing function. Just don't forget to pass the vector by constant reference, so it can be used on temporary values (such as sums) as well.

Vector3D Normalize(const Vector3D & arg) {  return arg / Length(arg);}


I disagree. An argument can be made that binary functions like cross and dot should be made free-standing, but it mades more sense to me to provide these functions as part of the vector. Would you make std::string::size() a free standing function?

However, I think the name 'Normalize' is misleading because it sounds like a verb that will modify the vector. I would use Vector3D::GetNormalized() for what you're trying to do, and Vector3D::Normalize() for one that modifies it in-place.

Edit: Trying to get back to the original post, I can say that there isn't anything wrong with the code you posted. I use exactly the same functions/expressions in my code and it works fine.
You can totally use operators "new" and "delete" with references:

friend Vector3D& operator+(const Vector3D& v1, const Vector3D& v2){   return *(new Vector3D(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z));}//more examples:Vector3D& ref = *(new Vector3D()); //assign a pointer to a referenceVector3D* ptr = &ref;              //assign a reference to a pointerdelete &ref;                       //delete a heap object using a referencedelete ptr;                        //delete a heap object using a pointer


I don't know if it's such a good idea though, references are kind of tricky.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
Those are seriously bad ideas, because you're putting in memory management responsibility in a place where (a) it's totally unnecessary, and (b) it's totally *unexpected*.

OP: Your implementations are fine (assuming the operator+= is a member function defined within the class body) and there's nothing there (if I'm thinking straight) that would prevent your second usage example from working. Show the Normalize() that you have, please.

'*this' is "a reference and not a pointer" because 'this' is a pointer. Remember that all those *'s and &'s are never part of a variable's name; they are either a part of the *type*, or an *operation* being performed. In this case, the '*' is a dereference of the pointer. Due to an unfortunate accident of terminology, "dereferencing" a pointer yields a reference (okay, it yields a value, which is assignable to a reference). The idea behind references is that they alias existing things, but provide value semantics.

Your second "method" (we say "member function" in C++) doesn't and *shouldn't* return a reference. A binary sum (as opposed to addition-to-existing-thing) logically creates a new value (the sum), and there's no good reason to (and many good reasons not to) invoke 'new' for that. The attempt you made at returning a reference is no good because 'new' returns a pointer to the allocated memory, and pointers *are not references*. But anyway, you don't want to return a reference to a temporary. Bad mojo, just like returning a pointer to a local.

The idea behind a reference is to *refer to* something that already exists. This is why we return a reference from operator+= : the semantics of this operator are that we *modify* the thing that's on the left-hand side. We return it by reference so that the result of the expression *is* the left-hand side, as opposed to being a separate value that is merely *equal to* the (newly modified) left-hand side.

'this' *should* be a reference, considering it from a usability perspective and in terms of what it can safely do, but it's a pointer. This is for historical reasons: quite simply, the idea of 'this' was added to the language before the idea of "references".

You should also read this.
Quote:Would you make std::string::size() a free standing function?


Why not? Although, to be honest, my view is probably biased by my mathematics background. In the end, size does not need to be either a member or a free standing function: a convention exists to make it a member in C++, but as long as it is not runtime-polymorphic (which it isn't) there is no advantage or disadvantage to making it a method (beyond the fact that it might be shorter to write).

Quote:However, I think the name 'Normalize' is misleading because it sounds like a verb that will modify the vector. I would use Vector3D::GetNormalized() for what you're trying to do, and Vector3D::Normalize() for one that modifies it in-place.


GetNormalize would make more sense as a member function.

Quote:Edit: Trying to get back to the original post, I can say that there isn't anything wrong with the code you posted. I use exactly the same functions/expressions in my code and it works fine.


Except that his Normalize method might be non-const.

I'm willing to bet his Normalize() function returns a void :)

This topic is closed to new replies.

Advertisement