In C, the only thing a structure could do was allow to you store multiple variables in one structure and allow for bitfields.
A class/struct in C++ is much much more.
First, there is the introduction of member functions. In C, if you had a player struct, for example, and you wanted it to "attack," you'd create an attack function and pass the address of the struct to the function.
In C++, however, there is the introduction of member functions. Basically, what that allows you to do is think of the Player as attacking rather than "attack" being a procedure which takes a parameter of a player.
////////
C Style
Attack( &MyPlayer ); // Attack with my player
C++ Style
MyPlayer.Attack(); // Make MyPlayer attack
////////
It is important to note that, at this point, both examples do the same thing internally, it's just the way of thinking about the interaction that's different. It makes you think more of the player as being an object that can actually do things, rather than just being a clump of data used for a procedure.
Then, there is the concept of varying access. What if you had a member of the struct that you used interally, but you don't want people to have direct access to -- you only want it to be able to be accessed by a member function like attack in the C++ example. A better example of this would be a class that represents an array that can be of different lengths and different places in memory. You wouldn't want people to be able to access that data directly because if they change it without using a member function, they might "break" the object. IE -- you don't want someone to directly change the size variable from 3 to 4 without reallocating the array.
The same concept is used with functions -- you might want a function that can only be used internally rather than by other people who are just using an instance of the class directly.
So, you have access specifiers. In C++ there are 3:
public
private
protected
For now, I'll just go into the first 2.
public gives you all access to the variable
private makes it so that only the class can access the variable
The way you use them is by placing the access specifier name followed by a colon within the class definition followed by any variables and/or functions that you want to have that type of access.
Code:
class IntArray
{
public:
void Resize( unsigned int NewSize ); // Can be accessed through an object
private:
int* DynamicArray; // Can't be accessed externally
unsigned int Length; // Can't be accessed externally
};
I left out the implementation for simplicity, and as you can see, the array can't do much right now. You can't even access any indices in the array yet!
But, if you have experience with dynamic memory allocation right now, there should be another problem that you can see:
If you don't give access to the DynamicArray pointer, how do you use it to allocate memory and deallocate the memory once it's allocated?
Once solution might be to make 2 functions -- one for allocating and one for deallocating that can be used externally... but there is a better way in C++. I'll leave behind the array example for now, but I'll get back to it.
Introducing constructors and destructors!!!
Back in C, in order to initialize a struct you had to do something similar to the way you initialized an array:
Code:
struct Player
{
int CurrentHealth;
int CurrentArmor;
}; // This was the Player struct declaration
Player MyPlayer = { 100, 50 }; // Initialize CurrentHealth to 1 and CurrentArmor to 50
The problem with this is that it forces the user to know exactly the implementation of the player structure and even the order. It also only allow one way for the person to construct the Player. What is, for instance, you wanted to pass it an enumerated value to represent a common preset health/Armor that the structure would be able to figure out the Armor and Health the player should have?
In C++ you can do that with constructors:
The concept is, every time you create an object, a function used for initializing the object is called! You have the control of what the constructor does, and you can even make multiple constructors -- as many as you want and for all different situatons.
The way you make a constructor is you create a function with no return value (not even void), that takes whatever parameters you want!
Code:
enum CommonHealth { NoHealth = 0, FullHealth = 1 };
class Player
{
public:
Player( CommonHealth HealthInit );
private:
int CurrentHealth;
int CurrentArmor;
};
Again, I'm leaving out the implementaton of the function, but I'll get back to it in a second.
Player MyPlayer( FullHealth ); // Construct the object with "FullHealth"
Before I go any further, let's talk about how to actually define these "member functions." It's very similar to defining standard functions. You specify the return value followed by the classname followed by the double colon scope operator followed by the name of the member function and it's parameters.
Code:
enum CommonHealth { NoHealth = 0, FullHealth = 1 };
class Player
{
public:
Player( CommonHealth HealthInit );
int GetHealth() const; // Return the current health. I'll explain const later
private:
int CurrentHealth;
int CurrentArmor;
};
Player::Player( CommonHealth HealthInit )
{
if( HealthInit == 0 )
CurrentArmor = CurrentHealth = 0;
else
if( HealthInit == 1 )
{
CurrentHealth = 100;
CurrentArmor = 50;
}
}
int Player::GetHealth() const
{
return CurrentHealth;
}
So now, you can, for instance do
Player MyPlayer( FullHealth );
printf( "%d", MyPlayer.GetHealth() );
and you will output the amount of health the player has (I'm assuming you are familiar with printf, if not, you just have to know that all it will do is print out whatever MyPlayer.GetHealth() returns as an integer).
In this case, it should output 100 because that's what we made the constructor set the value to when passed "FullHealth" as a parameter.
Now -- const
I'm sure you've used const before, so I'll make this quick. Going back to the original C example:
C Style
Attack( &MyPlayer ); // Attack with my player
C++ Style
MyPlayer.Attack(); // Make MyPlayer attack
What if you wanted to be able to pass a pointer to a constant player to the attack function? The attack function will not alter any of the player data, so you should be able to pass a constant.
In C, you know how to do this:
Code:
// Declare the function to use a pointer to a constant
void Attack( const Player* Attacker );
Well, in C++, since the "Attacker" parameter is implicit, you have to specify this elsewhere. So, you put the word "const" after the function name and parameter list. Look back to the GetHealth funciton for an example of this.
Okay, now that we have talked about constructors, let's get into a case where you'd want to "destruct" your data. The most common time you'd have to do this is with dynamic memory allocation -- like the IntArray example I gave earlier. The syntax for declaring a destructor is similar to that of a constructor. You create it like a member function without a return type, only this time, you give it the name of the classtype preceeded by a tilde ~. You define it just like any other member function. There can be only 1 destructor, unlike constructors, and the destructor is automatically called when the object is deleted.
So, let's get back to that IntArray example!
Code:
class IntArray
{
public:
IntArray( unsigned int LengthInit ); // Make a constructor for the initial size
~IntArray(); // Make a destructor for deallocating memory
void Resize( unsigned int NewSize ); // Can be accessed through an object
private:
int* DynamicArray; // Can't be accessed externally
unsigned int Length; // Can't be accessed externally
};
IntArray::IntArray( unsigned int LengthInit )
: Length( LengthInit ) // This initializes the length datamember to LengthInit.
{
DynamicArray = new int[Length]; // This is how you dynamically allocate in C++
}
IntArray::~IntArray()
{
delete [] DynamicArray; // This is how you dynamically deallocate arrays in C++
}
So, with that code, we can now do:
Code:
int main()
{
IntArray( 5 ); // Make an array of length 5
return 0;
}
This will create an array of length 5, and then, when main is finished, the destructor will get called and deallocate the memory!
Now, everything so far is really just syntactical difference from C, except for the automatic calling of the constructors/destructor. Now let's go into the fun stuff -- what really makes OOP powerful!!!
Inheritance...
Let's say you want enemies in your game. More importantly, let's say you have enemies of different types. They are all related because they are all "enemies," but how do we represent this relationship in C++
I'm going to through out a quick example with no implementation just yet, just so you can see the relationship between the objects and the syntax.
Code:
class Enemy // A type that we will derive other "enemies" from
{
public:
int GetHealth() const;
private:
int Health;
};
class SmallEnemy
: public Enemy // SmallEnemy is a kind of Enemy
{
};
class BigEnemy
: public Enemy // BigEnemy is also a kind of Enemy
{
};
Before going any further, just think about the concept.
You have a sort of abstract concept of an enemy, and you have two kinds of enemies -- a big enemy and a small enemy.
Notice that we said "enemy" has health. We said that SmallEnemy and BigEnemy are both "kinds of" enemies. Does this mean that they have health and a function "GetHealth" as well?
Yes!!!
That's all well and good... but right now, all we've done is saved us a couple of lines of typing... There's much more power to it than that...
Take this example...
Code:
Enemy MyEnemy;
SmallEnemy MySmallEnemy;
BigEnemy MyBigEnemy;
MyEnemy.GetHealth(); // Works
MySmallEnemy.GetHealth(); // Works
MyBigEnemy.GetHealth(); // Works
Enemy* AnEnemy;
AnEnemy = &MyEnemy; // We know this works
// ... Hmm... but maybe...
AnEnemy = &MySmallEnemy;
AnEnemy = &MyBigEnemy;
// Do they work? ...Yes!
We have just discovered that a pointer to an enemy can now point to a big enemy OR a small enemy as well!!! That means anythime we have a function that takes as a parameter a pointer to an enemy, we can pass a pointer to a small enemy or a big enemy as will! You've just made your game quite a bit more modular!
Okay... now, remember when I said there were 3 access specifiers
public
private
protected
I only talked about public and private because protected only comes into play with this "inheritance"
What protected means is that you can access the data in a "base class" from a "child" class, unlike private.
so
Code:
class Privates
{
private:
int Data;
};
class DerivedPrivates
: public Privates
{
public:
int GetData() const;
};
class Protecteds
{
protected:
int Data;
};
class DerivedProtecteds
: public Protecteds
{
public:
int GetData() const;
};
int DerivedPrivates::GetData() const
{
return Data; // Error, can't access Data
}
int DerivedProtecteds::GetData() const
{
return Data; // Success
}
Also, notice the "public" when used with the inheritance itself -- I'll touch on that in a bit.
Let's get back to the enemy example.
As I said, we have 2 types of enemies. We know that both kinds of enemies have to be able to attack, but what if we want them to attack... differently. Can we just redefine the function in "child" types? If so, will the appropriate function be called with the proper object... what if it's through a pointer to the base, will the appropriate function be called there as well? Imagine that the BigEnemy might attack with a battleaxe instead of an Enemy's sword -- if we assign the address of a BigEnemy to an EnemyPointer and called the Attack function, will he use a sword (as defined by the Enemy type) or a battleaxe (as defined by the BigEnemy type)? If it uses the battleaxe, imagine the power that gives you, as a programmer!
Code:
class Enemy
{
public:
void Attack();
int GetHealth() const;
private:
int Health;
};
class SmallEnemy
: public Enemy
{
public:
void Attack();
};
class BigEnemy
: public Enemy
{
public:
void Attack();
};
/* Let's now stop and say that we defined Attack differently for each of the objects.
I won't actually go and define them now, because it would take too long (and this
post is already a bit longer than I think you were expecting, heh) */
int main()
{
Enemy SomeEnemy;
SmallEnemy SomeSmallEnemy;
BigEnemy SomeBigEnemy;
SomeEnemy.Attack(); // Calls the Enemy's unique attack function
SomeSmallEnemy.Attack(); // Calls the SmallEnemy's unique attack function
SomeBigEnemy.Attack(); // Calls the BigEnemy's unique attack function
// Now let's make things interesting...
Enemy* EnemyPointer;
EnemyPointer = &SomeEnemy;
EnemyPointer->Attack(); // Calls the Enemy's attack function
EnemyPointer = &SomeSmallEnemy;
EnemyPointer->Attack(); // DAMN! It calls the Enemy's attack function
EnemyPointer = &SomeBigEnemy;
EnemyPointer->Attack(); // DAMN! It calls the Enemy's attack
return 0;
}
That test didn't go over too well... but don't be discouraged!!! There actually is a way to do what you want, and all you have to do is use one simple little word.
virtual
Code:
class Enemy
{
public:
virtual void Attack(); // notice the keyword virtual
int GetHealth() const;
private:
int Health;
};
class SmallEnemy
: public Enemy
{
public:
void Attack();
};
class BigEnemy
: public Enemy
{
public:
void Attack();
};
int main()
{
Enemy SomeEnemy;
SmallEnemy SomeSmallEnemy;
BigEnemy SomeBigEnemy;
SomeEnemy.Attack(); // Calls the Enemy's unique attack function
SomeSmallEnemy.Attack(); // Calls the SmallEnemy's unique attack function
SomeBigEnemy.Attack(); // Calls the BigEnemy's unique attack function
// Now let's make things interesting...
Enemy* EnemyPointer;
EnemyPointer = &SomeEnemy;
EnemyPointer->Attack(); // Calls the Enemy's attack function
EnemyPointer = &SomeSmallEnemy;
EnemyPointer->Attack(); // !!! It calls the SmallEnemy's attack function
EnemyPointer = &SomeBigEnemy;
EnemyPointer->Attack(); // !!! It calls the BigEnemy's attack
return 0;
}
Great!!! We now have a way of thinking about all the different kinds of enemies as "enemies," can store them on like terms, and be able to use varying versions of functions on them!!!
Just think of some of the uses -- you can now have an array of enemies, cycle through them -- calling each of their attack functions, and depending on whether they are BigEnemies, SmallEnemies, or any number of an infinite amount of enemy types there are, the appropriate version of "attack" gets called. Oh, the mighty power of virtual! 
Great! Now, let's say you want to derive one class from MULTIPLE other classes. For instance, what if you have a car class and a van class. You want a minivan. Let's derive the minivan from BOTH the car AND the van. All you have to do is separate the parent classes by a comma in the derivation.
Code:
class Car
{
};
clas Van
{
};
class MiniVan
: public Car, public Van // Derive from both a Car and a Van
{
};
Multiple inheritance gives a lot of power, and it's most oftened not used to directly mean a MiniVan is both a Car and a Van, but rather, to just combine two different implementations.
But with multiple inheritance comes a lot of problems, though not ones without solutions.
Say you want a minivan like the previous example, but your Car and MiniVan are both derived from a "Vehicle" class. That means that any datamembers that were in the base class would have two instances int the minivan class (one from the Car and one from the MiniVan) and their names would be the same. Now, not only do you have two variables where you only want one, but when you use the name it's ambiguous! which one are you referring to!
So, the keyword virtual comes back, only this time in a different way.
Code:
class Vehicle
{
public:
int Data;
};
class Car
: public virtual Vehicle
{
};
class Van
: public virtual Vehicle
{
};
class MiniVan
: public Car, public Van
{
};
Now:
MiniVan MyVehicle;
MyVehicle.Data; // Now refers to one single Data shared by the "Car part" and the "Van part" of MiniVan.
...Oh yeah, and structs default to public access while classes default to private...
EDIT: Long comment in code forced horizontal scrolling in browser