In the previous chapter, we discussed how we can approach programming by way of simulating objects that exist within the thing our program is trying to simulate.
For example, were we making a fantasy game, we might be simulating objects like monsters, weapons, and magical spells.
We saw how the way we represent these objects could be through:
// Variable
int Health{150};
// Function
void TakeDamage(int Damage){
Health -= Damage;
}
Our game might have thousands of objects. We don't want to define tens of thousands of variables and functions to manage this.
Instead, we want to write code that can apply to large sets of these objects. For example, maybe 100 of our objects will be monsters that the player can fight.
All the monster objects in our game likely share a lot of similar characteristics - for example, they may all have Health
variables, and they all have TakeDamage()
functions.
So, rather than writing code for 100 individual monster objects, we write code that works for all monsters, which they can then share.
This process of generalization, where we take specific objects and group them into more general categories is called abstraction
What is Abstraction?
We already saw examples of abstraction in the built-in types we’ve been using. For example, int
is an abstraction.
C++ compilers didn’t have to define code for every individual integer, like 3
, 42
, and 53,195
. Rather, they created an abstract concept of what an integer is, and the things an integer can do.
All integer objects then share those capabilities - like the ability to be incremented using the ++
operator or be logged to the terminal using the <<
operator.
The int
data type is what provides that abstraction. Similarly, bool
provides an abstraction for true and false values, whilst string
abstracts blocks of text.
Just as C++ provides some built-in types that are useful to a wide range of programs:
int Level;
bool isAlive;
string Name;
So too does it give us the ability to create user-defined types. These are types that we can set up to generalize the types of objects that will exist in our specific project:
Monster Goblin;
Weapon IronSword;
Spell Fireball;
A class is the main way we define a custom type. To create a user-defined type, such as Monster
, we would create a class
like this:
#include <iostream>
using namespace std;
class Monster {
public:
// Class code here
};
int main() {
// We can now create Monsters
Monster Goblin;
}
We describe the meaning of the public:
line later in this chapter. For now, let's just ensure it is included at the top of our classes.
How can we define a class called Weapon
in C++?
There is often some confusion at this point about what differentiates an object from a class. This will become clear as we start creating classes and objects in future lessons, but let's discuss it briefly here.
The English definition of class is: a category of things having some property or attribute in common
It means the same thing in programming. A class defines an abstract category of things that are, in some way, similar. The things within that category are objects.
Consider this example.
Monster Bonker;
Monster Basher;
Here, Monster
is a class. It is not a specific monster - it is an abstract idea of what it is to be a monster. It includes a description of what variables and functions all monsters have.
A class also grants the ability to create new objects of that class. Above, we’re using that ability to create two specific monsters. Bonker and Basher are both objects.
Because classes are used to construct objects, people often conceptualize classes as blueprints when explaining the concept. The class is a blueprint for creating a specific type of object. Many objects can be created from the same blueprint.
Creating an object from a class is sometimes referred to as instantiating the class. The objects created are sometimes called instances.
Above, we instantiate the Monster
class to create Bonker and Basher.
Bonker and Basher are instances of the Monster
class.
What is a Class?
What statement allows us to create a new Weapon object from this class?
class Weapon {
public:
}
To provide objects of our classes with the ability to store their state and perform actions, we need to define those variables and functions as part of our class.
Variables and functions that belong to a class are sometimes referred to as class members.
The syntax we use for class members is identical to what we’re familiar with in the previous chapter. We just place our variables and functions within the curly braces of a class definition.
For now, we should also ensure it is below the public:
line within our class:
class Monster {
public:
// a variable
int Health { 150 };
// a function
void TakeDamage(int Damage) {
Health -= Damage;
}
};
There are lots of different terms people use for class members.
Variables within a class are sometimes referred to with names like data member, field, or property
Similarly, a function that is associated with a class will sometimes be referred to using names like method or member function
These all broadly relate to the same ideas, and the meaning should hopefully be clear from the context.
Once we’ve created an object from our class, we will want to access the variables and functions that are granted to our object by the class.
We do that through the member access operator, which is a simple period: .
For example, accessing the Health
variable of a Monster
object would look like this:
Monster Bonker;
Bonker.Health;
Calling a function would look like this:
Monster Bonker;
Bonker.TakeDamage();
Below, we show these concepts in a simple program:
#include <iostream>
using namespace std;
class Monster {
public:
int Health{150};
void TakeDamage(int Damage){
Health -= Damage;
}
};
int main(){
Monster Bonker;
cout << "Bonker Health: " << Bonker.Health;
Bonker.TakeDamage(25);
cout << "\nBonker Health: " << Bonker.Health;
};
Bonker Health: 150
Bonker Health: 125
We can use a class variable in the same way we use any other variable of that same type. Above, Bonker.Health
is an int
, so we can use it like any other int
. For example, we can:
++
to increment it!=
to compare it to another number, generating a boolean resultImagine we had the following code:
class Weapon {
public:
int Damage { 50 };
};
Weapon IronSword;
What statement allows us to access the Damage
integer of our object?
A common point of confusion at this point might be how exactly class variables are "shared" among the objects of that class.
Within our class definition, when we declare a variable in the way we’ve been demonstrating, we are stating that every object of our class will have a variable with that name and type.
However, each object will have its own copy of that variable. So in this example, every Monster
object will have an int
called Health
, but the value of that variable will represent that specific object’s Health
.
When we set an initial value for a variable within the class definition, what we are doing is simply specifying what the initial value of that variable will be for every new object we create.
#include <iostream>
using namespace std;
class Monster {
public:
int Health{100};
};
int main(){
Monster Bonker;
Monster Basher;
cout << "Bonker Health: " << Bonker.Health;
cout << "\nBasher Health: " << Basher.Health;
Bonker.Health = 50;
cout << "\nBonker Health: " << Bonker.Health;
cout << "\nBasher Health: " << Basher.Health;
};
Bonker Health: 100
Basher Health: 100
Bonker Health: 50
Basher Health: 100
In the previous chapter on forward declarations, we introduced how a function can be declared and defined in different places in our code.
We can apply this same technique to class functions too, should we need to. Within our class definition, we provide the prototype for our class function:
class Monster {
public:
int Health { 150 };
// Declaration
void TakeDamage(int Damage);
};
We can then provide the definition elsewhere in our code.
The only difference is, that when we’re defining a class function, we need to additionally provide the class name. We do that by using the ClassName::FunctionName
pattern.
For example, to define the TakeDamage
function within the Monster
class, we would do this:
// Definition
void Monster::TakeDamage(int Damage) {
Health -= Damage;
}
The definition doesn’t need to be within the class and, as we’ll see later, it doesn’t even need to be in the same file.
The key points covered in this lesson were:
int
, bool
, and string
, showcasing them as examples of abstraction..
) to interact with an object's variables and functions.In the upcoming lesson, we'll delve into access modifiers, which control the visibility and accessibility of class members.
public:
modifier, as seen in this lesson, and introduce other modifiersLearn how to define, instantiate, and utilize classes, understanding how they form the backbone of object-oriented programming.
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way