Classes

In this lesson we will learn what classes are and why they make C++ such a code powerhouse.

Object-Oriented Programming

Until now all of our C++ examples have involved declaring variables in the main() function. However, most games are coded with an object-oriented design. This means that most of our variables and functions are stored in classes. Instead of declaring variables for the player's health and ammo, we can create a player class and store all values in it:

class player
{
public:
int health;
int ammo;
};

We can create multiple players, and each one will have its own variables for health, ammo, and anything else we add. These values are called members.

Creating a Class

A new C++ class involves two files. The .cpp file will contain the code for all class functions (methods) and the header file will declare the class, listing all its members and methods.

In your game's Source folder, create two new text files named "Monster.cpp" and "Monster.h". Drag these files onto the project explorer in Visual Studio to add them to your C++ project.

In the Monster.h file add this code:

#include "Leadwerks.h"

class Player
{
public:
int health;
int ammo;
void TakeDamage(int damage);
};

In the Monster.cpp file add this code:

#include "Monster.h"

using namespace Leadwerks;

void Monster::TakeDamage(int damage)

{

health -= damage;

}

We need to include our class header in the main.cpp file. Replace all the code in that file with this:

#include "Leadwerks.h"

#include "Monster.h"

using namespace Leadwerks;

int main(int argc,const char *argv[])

{

return 0;

}

Objects and Pointers

Okay, this is going to be a little weird. This may be the weirdest idea to get used to in C++.

There's two ways we can handle objects. We can treat tham as a persistent object that needs to be specifically created and destroyed:

Monster* monster = new Monster;

player->TakeDamage(10);

delete monster;

The asterisk signifies that the player variable above is a pointer to an object. That object stays in memory (RAM) until it is removed using the delete command. If we don't delete the object, it will remain in memory indefinitely. Memory leaks happen when we create more and more objects but forget to delete them.

We can also treat an object as a regular old variable, in which case we don't have to worry about cleaning it up:

Monster monster;

monster.TakeDamage(10);

You don't worry about deleting a float variable, do you? In the same manner, the Monster object above does not need to be deleted by the programmer.

When you make an object equal to another object, the contents of the value are copied, but you still have two separate objects. In the example below, changing the health value of monster1 won't change monster2, because they are separate objects:

Monster monster1;

monster2 = monster1;

monster1.health = 50;

//Prints 50
Print(monster1.health);

//Prints 100
Print(monster2.health);

In the example below, changing the health member of monster1 will change the health member of monster2...because they are both pointers to the same object.

Monster* monster1 = new Monster;

Monster* monster2 = new Monster;

monster2 = monster1;

monster1->health = 50;

//Prints 50
Print(monster1->health);

//Prints 50
Print(monster2->health);

The basic rule is we use pointers for complex, persistant objects we create and destroy, and we use object variables for simple mathematics classes, like Vec3, Vec4, Mat4, etc.

The code below feels good:

Vec3 a = Vec3(1,2,3);

Vec3 b = Vec3(4,5,6);

Vec3 c = a + b;

The code below works, but it does not feel good:

Vec3* a = new Vec3(1, 2, 3);

Vec3* b = new Vec3(4, 5, 6);

Vec3* c = new Vec3;

c->x = a->x + b->x;

c->y = a->y + b->y;

c->z = a->z + b->z;

delete a;

delete b;

delete c;

Also notice that pointer variables don't have any value until they are initialized. Before that, they will just be a random memory address. This is a very bad situation because it can give you a lot of unpredictable behavior that is difficult to identify:

Vec3* v;
As we've said before, always initialize your pointers by either creating a value immediately or setting them to NULL (or nullptr):

Vec3* a = new Vec3;

Vec3* b = NULL;

Uninitialized pointers are one of the most common source of problems in C++. Why aren't they automatically initialized to NULL by default? Because it's faster that way. You're in the big leagues now.