So since I'm learning about C++ from this nifty book I bought I thought I'd share a few tips here and there. Here I'm going to explain the basics of overloading operators and how it's useful along with some examples. Since you're using a C++ compiler for DarkGDK this will of course work with DarkGDK as well.
First of all, why would anyone want to overload an operator? You'll be able to add more functionality to the usual operators found in C++ (such as + - * / && << >> || etc.). You'd be able to change this:
// add vectors together and store in result vector
vec3 result;
result.x = myVector1.x + myVector2.x;
result.y = myVector1.y + myVector2.y;
result.z = myVector1.z + myVector2.z;
to this:
// add vectors together and store in result vector
vec3 result;
result = myVector1 + myVector2;
Section 1
Here's a small example. Let's assume you have some vector calculations you want to do:
// Overloading Operators
// by TheComet
#include <iostream>
#include <iomanip>
using namespace std;
// ---------------------------------------------------------------------------------
// vector 3 class
class vec3
{
public:
// vector 3 elements
float x;
float y;
float z;
// constructor, destructor
vec3( void );
vec3( float x, float y, float z );
~vec3();
};
// ---------------------------------------------------------------------------------
// ability to set values on creation in the constructor
vec3::vec3(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
}
// ...or not
vec3::vec3( void )
{
}
// ---------------------------------------------------------------------------------
// destructor
vec3::~vec3()
{
}
// ---------------------------------------------------------------------------------
// main entry point (I think DarkGDK has some other form of this)
int main( int argc , char* argv[] )
{
// define 2 vector objects
vec3 myVector1(4, 4, 4);
vec3 myVector2(6, 7, 3);
// add vectors together and store in result vector
vec3 result;
result.x = myVector1.x + myVector2.x;
result.y = myVector1.y + myVector2.y;
result.z = myVector1.z + myVector2.z;
// output vectors
cout << "Vector 1 : " << myVector1.x << "," << myVector1.y << "," << myVector1.z << endl;
cout << "Vector 2 : " << myVector2.x << "," << myVector2.y << "," << myVector2.z << endl;
cout << "result : " << result.x << "," << result.y << "," << result.z << endl;
// end program
cin.ignore();
return 0;
}
This is a simple example of adding 2 vectors together, which is something that happens a lot in 3D games. In particular look at this section:
// add vectors together and store in result vector
vec3 result;
result.x = myVector1.x + myVector2.x;
result.y = myVector1.y + myVector2.y;
result.z = myVector1.z + myVector2.z;
Wow, that's a bit silly if we have to do that for every vector in the code... This is where overloading becomes handy. Usually you'll see people create some kind of function to solve the problem, perhaps something like this:
// Overloading Operators
// by TheComet
#include <iostream>
#include <iomanip>
using namespace std;
// ---------------------------------------------------------------------------------
// vector 3 class
class vec3
{
public:
// vector 3 elements
float x;
float y;
float z;
// constructor, destructor
vec3( void );
vec3( float x, float y, float z );
~vec3();
};
// ---------------------------------------------------------------------------------
// ability to set values on creation in the constructor
vec3::vec3(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
}
// ...or not
vec3::vec3( void )
{
}
// ---------------------------------------------------------------------------------
// destructor
vec3::~vec3()
{
}
// ---------------------------------------------------------------------------------
// function for adding vectors together
vec3 AddVector( vec3 vector1, vec3 vector2 )
{
vec3 result;
result.x = vector1.x + vector2.x;
result.y = vector1.y + vector2.y;
result.z = vector1.z + vector2.z;
return result;
}
// ---------------------------------------------------------------------------------
// main entry point (I think DarkGDK has some other form of this)
int main( int argc , char* argv[] )
{
// define 2 vector objects
vec3 myVector1(4, 4, 4);
vec3 myVector2(6, 7, 3);
// add vectors together and store in result vector
vec3 result;
result = AddVector( myVector1, myVector2 );
// output vectors
cout << "Vector 1 : " << myVector1.x << "," << myVector1.y << "," << myVector1.z << endl;
cout << "Vector 2 : " << myVector2.x << "," << myVector2.y << "," << myVector2.z << endl;
cout << "result : " << result.x << "," << result.y << "," << result.z << endl;
// end program
cin.ignore();
return 0;
}
But that's still not the cool way of doing it. Now you could argue that you could make the function inline and all of that to speed things up, but that just complicates things.
The solution? We are going to overload the + (plus) operator. First step to doing this is declaring it in the class like this:
// ---------------------------------------------------------------------------------
// vector 3 class
class vec3
{
public:
// vector 3 elements
float x;
float y;
float z;
// overload operators
const vec3 operator+( const vec3 &e ) const;
// constructor, destructor
vec3( void );
vec3( float x, float y, float z );
~vec3();
};
Note that we used an & symbol in front of "e". This is something only C++ can do and is known as a "reference", meaning that we're not actually using the data of e, but e is a reference to the object passed. We're changing the actual data of the object, not a copy of it. I guess I could go deeper into it but that's something for another time. Just trust me that it should be there.
You'll also notice that I've declared the returned data as
const vec3, e as a
const vec3& and the method as
const. This is, respectively, so the returned data can't be altered (prevents things like result + myVector1 = myVector1 + myVector2), referenced data cannot be altered within the method, and the method cannot alter it's own data within the object.
So let's look closer at that new line:
vec3 operator+( vec3 &e );
An operator overloaded function looks just like any other C++ function, with just one exception: the name of the function must be
operator, followed by the operator you want to overload. In other words, you can use
operator to overload any operator you want:
operator+();
operator-();
operator*();
operator/();
operator<<();
etc.
Just like functions, the
vec3 at the beginning means that it returns a data type of "vec3". Since we're declaring the overload inside the class, the left argument is already given (namely the contents of the class), which means that it only requires the right argument to be complete, which is
vec3 &e.
Next step, naturally, is to define the function. Here it is:
// ---------------------------------------------------------------------------------
// Overload plus operator
const vec3 vec3::operator+( const vec3 &e ) const
{
vec3 result;
result.x = x + e.x;
result.y = y + e.y;
result.z = z + e.z;
return result;
}
That looks much like the function above I just told you
not to use. In fact, it's exactly the same code except that the function is now called
operator+. The reason why will become apparent in this next piece of code. I don't think the above needs explaining, as it's the standard way of defining functions.
The plus operator is now overloaded. So what the hell did all of that just do? Well, instead of this:
// add vectors together and store in result vector
vec3 result;
result.x = myVector1.x + myVector2.x;
result.y = myVector1.y + myVector2.y;
result.z = myVector1.z + myVector2.z;
We can now simply write this:
// add vectors together and store in result vector
vec3 result;
result = myVector1 + myVector2;
Awesome, right? Here's the entire code for that:
// Overloading Operators
// by TheComet
#include <iostream>
#include <iomanip>
using namespace std;
// ---------------------------------------------------------------------------------
// vector 3 class
class vec3
{
public:
// vector 3 elements
float x;
float y;
float z;
// overload operators
const vec3 operator+( const vec3 &e ) const;
// constructor, destructor
vec3( void );
vec3( float x, float y, float z );
~vec3();
};
// ---------------------------------------------------------------------------------
// ability to set values on creation in the constructor
vec3::vec3(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
}
// ...or not
vec3::vec3( void )
{
}
// ---------------------------------------------------------------------------------
// destructor
vec3::~vec3()
{
}
// ---------------------------------------------------------------------------------
// Overload plus operator
const vec3 vec3::operator+( const vec3 &e ) const
{
vec3 result;
result.x = x + e.x;
result.y = y + e.y;
result.z = z + e.z;
return result;
}
// ---------------------------------------------------------------------------------
// main entry point (I think DarkGDK has some other form of this)
int main( int argc , char* argv[] )
{
// define 2 vector objects
vec3 myVector1(4, 4, 4);
vec3 myVector2(6, 7, 3);
// add vectors together and store in result vector
vec3 result;
result = myVector1 + myVector2;
// output vectors
cout << "Vector 1 : " << myVector1.x << "," << myVector1.y << "," << myVector1.z << endl;
cout << "Vector 2 : " << myVector2.x << "," << myVector2.y << "," << myVector2.z << endl;
cout << "result : " << result.x << "," << result.y << "," << result.z << endl;
// end program
cin.ignore();
return 0;
}
Important: The + (plus) operator is still able to do normal additions. The only time when the overloaded version of it becomes active is when there are two data types of vec3 on both sides of it (such as "myVector1 + myVector2"). Otherwise it still performs as normal.
You can also add multiple vectors together no problem:
result = myVector1 + myVector2 + myVector3 + myVector4;
Section 2
So what else can we overload? Hmm, well THIS looks like a bit of work, doesn't it?
// output vectors
cout << "Vector 1 : " << myVector1.x << "," << myVector1.y << "," << myVector1.z << endl;
cout << "Vector 2 : " << myVector2.x << "," << myVector2.y << "," << myVector2.z << endl;
cout << "result : " << result.x << "," << result.y << "," << result.z << endl;
Let's overload << (insert operator) so we can just output our vectors directly like this:
cout << "Vector 1 : " << myVector1 << endl;
So the first problem you're going to notice is that until now we've only overloaded operators where we had control of both sides (myVector1 + myVector2). So how do we overload an operator that has already been overloaded by another class? We weren't the ones that defined "cout", so how do we overload it again? (cout << myVector1). The answer is the keyword
friend. Yes, C++ allows you to make friends with other classes. What does this do? It gives us access to the normally protected and private data of that class. Just an example demonstrating this:
class Cube
{
private:
int x;
int y;
friend Cube getPrivateData( Cube params );
};
Cube getPrivateData( Cube params )
{
Cube result;
result.x = params.x;
result.y = params.y;
return result;
}
int main( int argc, char* argv[] )
{
Cube myCube;
Cube Thief;
Thief = getPrivateData( myCube );
return 0;
}
See how
Thief is able to get the private data anyway? This is possible because the function
getPrivateData() was declared as a friend of the class.
So using this newly learned technique, let's apply that to our class so we can be friends with
ostream (which has the declaration of
cout in it). Here's the updated class:
// ---------------------------------------------------------------------------------
// vector 3 class
class vec3
{
public:
// vector 3 elements
float x;
float y;
float z;
// overload operators
const vec3 operator+( const vec3 &e ) const;
// overload output operator
friend ostream &operator<<( ostream &out, vec3 &e );
// constructor, destructor
vec3( void );
vec3( float x, float y, float z );
~vec3();
};
So what does this do?
friend ostream &operator<<( ostream &out, vec3 &e );
We're telling the compiler that
ostream is now a friend of our class, thus giving us access to it's private data. That allows us to use
cout as the left argument of <<. Apart from that, we declare the overload function as usual:
// ---------------------------------------------------------------------------------
// Overload output operator
ostream &operator<<( ostream &out, vec3 &e )
{
out << e.x << "," << e.y << "," << e.z;
return out;
}
And there you go! Now you can use << to output your vectors directly!
cout << myVector1 << endl;
Here's the entire code:
// Overloading Operators
// by TheComet
#include <iostream>
#include <iomanip>
using namespace std;
// ---------------------------------------------------------------------------------
// vector 3 class
class vec3
{
public:
// vector 3 elements
float x;
float y;
float z;
// overload operators
const vec3 operator+( const vec3 &e ) const;
// overload output operator
friend ostream &operator<<( ostream &out, vec3 &e );
// constructor, destructor
vec3( void );
vec3( float x, float y, float z );
~vec3();
};
// ---------------------------------------------------------------------------------
// ability to set values on creation in the constructor
vec3::vec3(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
}
// ...or not
vec3::vec3( void )
{
}
// ---------------------------------------------------------------------------------
// destructor
vec3::~vec3()
{
}
// ---------------------------------------------------------------------------------
// Overload plus operator
const vec3 vec3::operator+( const vec3 &e ) const
{
vec3 result;
result.x = x + e.x;
result.y = y + e.y;
result.z = z + e.z;
return result;
}
// ---------------------------------------------------------------------------------
// Overload output operator
ostream &operator<<( ostream &out, vec3 &e )
{
out << e.x << "," << e.y << "," << e.z;
return out;
}
// ---------------------------------------------------------------------------------
// main entry point (I think DarkGDK has some other form of this)
int main( int argc , char* argv[] )
{
// define 2 vector objects
vec3 myVector1(4, 4, 4);
vec3 myVector2(6, 7, 3);
// add vectors together and store in result vector
vec3 result;
result = myVector1 + myVector2;
// output vectors
cout << "Vector 1 : " << myVector1 << endl;
cout << "Vector 2 : " << myVector2 << endl;
cout << "result : " << result << endl;
// end program
cin.ignore();
return 0;
}
Outro
Keep in mind that this can be used with anything. It doesn't have to be vectors, this can be done to any class you declare.
I hope this was useful for you. This technique greatly simplifies your code and I urge you to use them instead of those ugly work-arounds with functions or #define statements.
TheComet
"if you don't understand recursion than you probably don't understand recursion." ~Jerico2day