c++ overloading plus in an efficient way - c++

I wrote a class like this:
class vector3D{
public:
double x,y,z;
}
and i overload + operator:
class vector3D{
public:
double x,y,z;
vector3D operator+(const vector3D &lhs)
{
vector3D temp(x+lhs.x,y+lhs.y,z+lhs.z);
return temp;
}
}
but using C=A+B is slower than
C.x = A.x + B.x;
C.y = A.y + B.y;
C.z = A.z + B.z;
i think it is because defining a vector3D instance in + overloading function. is there anyway to avoid this?
(FYI: i build with this flags: -Wall -Wextra -Wunreachable-code -m32 -DNDEBUG -O2 -D_LARGEFILE64_SOURCE -D_REETRANT -D_THREAD_SAFE)
EDIT:
this is how i test speed of two approach(in main() ):
//test bench
vector3D A(0.0,0.0,0.0), B(1e-9,1e-9,1e-9);
clock_t c = clock();
//case A
for(long int i=0;i<1000000000;i++)
{
A = A + B;
}
cout<<"case A took: "<<1.0*(clock()-c)/CLOCKS_PER_SEC<<A<<endl;
c = clock();
//case B
for(long int i=0;i<1000000000;i++)
{
A._x = A._x+B._x;
A._y = A._x+B._y;
A._z = A._x+B._z;
}
cout<<"case B took: "<<1.0*(clock()-c)/CLOCKS_PER_SEC<<A<<endl;
and the result is:
case A took: 5.539[1, 1, 1]
case B took: 1.531[2, 2, 2]

Since you're creating an additional object, it will carry some overhead. This is inherent.
However, looking at the "payload lines" you want, they are very similar to what you'd have in the body of operator+= adding some other:
vector3D &operator+=(const vector3D &other) // Note - no objects created.
{
x += other.x;
y += other.y;
z += other.z;
return *this; // Note - return by reference.
}
Of course, operator+= modifies its left operand (which is exactly why you have operator+ with its object-creation overhead).
In general, you should prefer operator+= for heavy objects where it is applicable (see also this question).

You may actually get a performance boost by collapsing this to one line and taking advantage of return value optimization:
vector3D operator+(const vector3D &lhs)
{
return vector3D(x+lhs.x,y+lhs.y,z+lhs.z);
}

Related

Which way to overload the operator more efficiently and why?

Which way more effective? Is there any difference this->X or just X? I think it 'void' version bcs compilator dont need to call contructor or smth, just add.
void operator+=(vect right)
{
this->x += right.x;
this->y += right.y;
}
void operator+=(vect right)
{
x += right.x;
y += right.y;
}
vect& operator+=(vect right)
{
x += right.x;
y += right.y;
return *this;
}
Start by not taking complex types as arguments by value if you care about efficiency.
vect& operator+=(vect const& right)
{
x += right.x;
y += right.y;
return *this;
}
this->x and just a plain x mean the same thing. They don't affect runtime at all. And finally, return vect& because it's idiomatic.
Version 1 and 2 do the exact same thing, no difference. Version 3 allows you to write
vecA += vecB += vecC += vecD;
There is no difference in terms of performance, the operator returns a reference to self, not a new object.

User defined calculation on structs/classes

I was once using Unity and they use a cool System to add and multiply vectors.
This is a short excerpt from the Reference (http://docs.unity3d.com/ScriptReference/Transform.html)
foreach (Transform child in transform) {
child.position += Vector3.up * 10.0F;
}
as you can see they can just add and multiply two Vectors (structs/classes) even though they could have different variables. Right now when I try to make such a calculation I have to add the x and y of a 2d float vector individually.
So how can i add structs like a
struct Vec2f{
float x;
float y;
};
together by writing
Vec2f c = a + b;
instead of
c.x = a.x + b.x;
c.y = a.y + b.y;
EDIT:
You need to overload operators like '+', '-' etc. in your struct. Example for addition:
struct Vec2f
{
float x;
float y;
Vec2f operator+(const Vec2f& other) const
{
Vec2f result;
result.x = x + other.x;
result.y = y + other.y;
return result;
}
};
EDIT 2:
RIJIK asks in comment:
Also i wonder if there are some tricks to make the functions not to be so repetetive. I mean if i now want to use an operator i have to make a function for each operator even if there is no big difference between the functions besides the operator which is used. How do you approach in implementing calculations or many operators?
So one thing you can do is use another operators in operator. Something like you can change subtraction to addition, by simply negate second number:
a - b becomes a + (-b)
Of course you need negation operator first in your struct to do that. Example code:
struct Vec2f
{
float x;
float y;
Vec2f operator+(const Vec2f& other) const
{
Vec2f result;
result.x = x + other.x;
result.y = y + other.y;
return result;
}
Vec2f operator-() const
{
Vec2f result;
result.x = -x;
result.y = -y;
return result;
}
// we use two operators from above to define this
Vec2f operator-(const Vec2f& other) const
{
return (*this + (-other));
}
};
One nice thing about this is that if you change addition for example than you don't need to change subtraction. It is changed automatically.
And as reply to this:
But i don't understand why i should make the functions constant.
Because then you can use this function with constant variables. For example:
const Vec2f screenSize = { 800.0f, 600.0f };
const Vec2f boxSize = { 100.0f, 200.0f };
const Vec2f sub = screenSize - boxSize;
I am using here also curly braces initialization, cause you don't define any constructor.

returning a reference vs the type

I was looking over some code, in a textbook and they wrote:
Vector3 &operator =(const Vector3 &a) {
x = a.x; y = a.y; z = a.z;
return *this;
}
Does the following code produce the same, returning the type, not a reference to it(they both run):
Vector3 operator =(const Vector3 &a) {
x = a.x; y = a.y; z = a.z;
return *this;
}
my question: what is the difference between the two?
thanks
daniel
Vector3 a, b;
(a = b).x = 3;
In this code, a.x should end up with the value of 3. In the second example you give, that won't happen.
Vector3 b(1,2,3);
Vector3 a;
(a = b).x += 2.0;
Print(a.x);
If you use operator returning refernce, above code should print 3.0
In case of operator returning value it would print 1.0

Multiplying with parenthesis result using overloaded * operator in C++

So I've been writing a small math library in C++ and when dealing with a scalar multiplied by a vector I get some issues when I try to perform this operation
Vect V2;
Vect V3;
float S;
Vect V1 = V2 + S * (V2 - V3);
The Vect Value I receive in the overloaded operator * is a new Vect object and not the outcome of the (V2 - V3) part of the operation. Here's the other relevant part of the code. If I follow the operation with the debugging the two overloaded operators work correctly by themselves but not one after the other.
Vect.h
Vect &operator - (const Vect &inVect);
friend const Vect operator*(const float scale, const Vect &inVect);
Vect.cpp
Vect &Vect::operator - (const Vect &inVect)
{
return Vect (this->x - inVect.x,this->y - inVect.y,this->z - inVect.z, 1);
}
const Vect operator*(const float scale, const Vect &inVect)
{
Vect result;
result.x = InVect.x * scale;
result.z = InVect.y * scale;
result.y = InVect.z * scale;
result.w = 1;
return result;
}
I also overloaded the + and = operator and they work as expected the only problem I encounter is the problem above.
In your operator-, you create a temporary Vect and return a reference to that temporary. The temporary is destroyed at the end of the return statement and the returned reference is left dangling. Doing anything with this reference will result in undefined behaviour.
Instead, your operator- should return a Vect by value:
Vect Vect::operator - (const Vect &inVect)
{
return Vect (this->x - inVect.x,this->y - inVect.y,this->z - inVect.z, 1);
}

Memory comparison, which is faster?

I have a 3D vector class. The private variables are defined:
union {
struct {
double x;
double y;
double z;
};
double data[3];
};
In implementing operator==, which is faster?
return this->x == v.x && this->y == v.y && this->z == v.z;
OR
return memcmp(this->data, v.data) == 0;
Unfortunately the two aren't equivalent. (Specifically NaNs and signed zeros don't use bitwise comparison inside the FPU).
So you should make your choice based on correctness, not speed.