Abstraction and Classes

Learn how to define, instantiate, and utilize classes, understanding how they form the backbone of object-oriented programming.
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
3D art showing a character looking into the distance
Ryan McCombe
Ryan McCombe
Edited

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:

  • variables, that represent the current state of the object
  • functions, that allow the object to perform actions
// Variable
int Health{150};

// Function
void TakeDamage(int Damage){
  Health -= Damage;
}

Abstraction

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

Test your Knowledge

Abstraction

What is Abstraction?

Built-in Types

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.

User-Defined Types

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.

Test your Knowledge

Creating Classes

How can we define a class called Weapon in C++?

Classes vs Objects

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.

Instances and Instantiating

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.

Test your Knowledge

Instantiating Classes

What is a Class?

What statement allows us to create a new Weapon object from this class?

class Weapon {
public:

}

Class Members

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;
  }
};

Class Member Terminology

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.

The Member Access Operator

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:

  • Pass it as an argument to a function
  • Copy its value to a new variable
  • Use the ++ to increment it
  • Use the != to compare it to another number, generating a boolean result
Test your Knowledge

Class Member Access

Imagine 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 Note on Class Variables

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

Class Function Prototypes

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.

Summary

The key points covered in this lesson were:

  • Introduced the concept of classes as fundamental to object-oriented programming.
  • Explained abstraction as the process of generalizing specific objects into broader categories, allowing for more efficient and manageable code.
  • Discussed built-in types in C++ like int, bool, and string, showcasing them as examples of abstraction.
  • Covered the creation of user-defined types through classes, enabling the definition of custom objects specific to a project's needs.
  • Clarified the difference between a class and an object, with a class being an abstract category and an object being an instance of that class.
  • Introduced the concept of instantiating a class to create objects, and the use of the member access operator (.) to interact with an object's variables and functions.
  • Demonstrated how to define class members, including both variables and functions.
  • Provided insights into class function prototypes, showing how functions can be declared within a class and defined elsewhere.

Preview of Next Lesson: Access Modifiers

In the upcoming lesson, we'll delve into access modifiers, which control the visibility and accessibility of class members.

  • We'll explore the public: modifier, as seen in this lesson, and introduce other modifiers
  • We'll see how public members can be accessed from anywhere, while private members are only accessible within the class itself.
  • We’ll see how this lets us achieve encapsulation - an important goal to keep complex projects manageable and pleasant to work on.

Was this lesson useful?

Edit History

  • Merged and rewrote "What is a Class?" and "Creating Classes" lessons

  • First Published

Ryan McCombe
Ryan McCombe
Edited
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
Classes and Structs
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 56 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

Encapsulation and Access Specifiers

A guide to encapsulation, class invariants, and controlling data access with public and private specifiers.
3D art of a character in a prison cell
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved