| Lesson 5 |
What is a class? |
| Objective |
Understand purpose of a class in C++ |
What Is a C++ Class? (Definition, Syntax, and struct Extension Explained)
A class is an extension of the idea of struct found in C. To facilitate encapsulation, the concept of struct is augmented in C++ to allow functions, as well as data, to be members. In OOP terminology, a member function is frequently called a method. For our immediate purposes, we can consider class and struct to be practically the same thing — we will be discussing how class differs from struct a little later in this module. For the next few lessons, we will use struct in our examples because struct is more familiar to C programmers. Lesson 4 established why encapsulation matters and what it achieves. This lesson examines the syntax — the class declaration format, how objects are instantiated from a class, and how a complete, realistic class definition models a complex real-world entity. The full Tank class at the end of this lesson demonstrates the same encapsulation principles in a domain directly relevant to game development.
A Class Is an Extension of struct
From C struct to C++ class
In C, a struct is a plain aggregate of data fields — all members are public by default, there are no member functions, and there is no access control. C++ preserves the struct keyword and its syntax but augments it significantly: a C++ struct can contain both data members and member functions, can have access specifiers, and can have constructors and destructors. The class keyword is nearly identical — the only behavioral difference is the default access level. In a struct, members default to public; in a class, members default to private. This single difference is the reason the C++ community uses class for types with invariants and struct for plain data aggregates, but both keywords are equally capable of expressing any C++ type. Lesson 9 of this module covers the private/public access distinction in detail.
Member Functions — Methods in OOP Terminology
A class is an expanded concept of a data structure — instead of holding only data, it can hold both data and functions. The functions defined inside a class are called member functions in C++ standard terminology. In OOP terminology more broadly, they are frequently called methods. Both terms refer to the same thing: a function that is part of a class definition, has access to the class's data members, and is called through an object of the class type. Throughout this module and course, both terms are used interchangeably — "member function" is the C++ standard term; "method" is the OOP general term.
struct and class — Practically the Same for Now
For the next few lessons, the examples in this module use struct because it is more familiar to developers coming from C. A struct in C++ works identically to a class with a public: label at the top — every member is accessible from outside by default. This makes the examples slightly simpler to read while introducing member functions and encapsulation concepts. Once the distinction between public and private has been fully established in lesson 9, the preferred C++ idiom — using class for types with private data — will be applied consistently.
Class Declaration Syntax
The class Keyword and Format
Classes are generally declared using the keyword class with the following canonical format:
class ClassConcept {
access_specifier_1:
member1;
access_specifier_2:
member2;
...
} object_names;
Where ClassConcept is a valid identifier for the class and object_names is an optional list of names for objects of this class. The body of the declaration can contain members that are either data or function declarations, and optionally access specifiers.
Elements of the Declaration — Explained
Each element of the class declaration has a specific role. The class keyword introduces the class definition — it tells the compiler that what follows is a user-defined type. The class name (ClassConcept in the template) is the identifier by which the type will be known — it follows the same naming rules as any C++ identifier. The opening brace { begins the class body. Access specifiers (public:, private:, protected:) followed by a colon control the visibility of the members that follow them. Member declarations inside the body define the data members (variables) and member functions (methods) of the class. The closing brace } ends the class body, optionally followed by a comma-separated list of object names to declare instances of the class immediately. The entire declaration ends with a semicolon.
The Semicolon — Required After the Closing Brace
The semicolon after the closing brace of a class definition is required and is one of the most common sources of confusing compiler errors for developers new to C++. Unlike function definitions — which do not end with a semicolon — class definitions, struct definitions, and enum definitions all require a terminating semicolon:
class Tank { ... }; // correct — semicolon required
void display() { ... } // function definition — no semicolon
Omitting the semicolon does not produce an error on the line of the class definition itself. Instead, the compiler sees the next statement as a continuation of the class declaration and produces a cascade of confusing errors in the code that follows — often on entirely unrelated lines. When confronted with a large block of errors following a class definition, the first thing to check is the presence of the terminating semicolon.
Classes, Objects, and Instantiation
Class as Type, Object as Variable
An object is an instantiation of a class. If one were to compare this with a procedural language, a class would be the type and an object would be the variable. The analogy is exact: just as int is a type and int x = 5 declares a variable of that type, Tank is a type and Tank armoredTank{...} declares a variable — an object — of that type. The class definition specifies the structure and behavior that every object of the type will have; the object declaration creates a specific instance with its own storage and its own copy of all data members.
Instantiation Creates Storage
The class definition itself does not create any storage — it is a blueprint, not an object. Storage is created only when an object is instantiated:
// Class definition — no storage created
class Tank { ... };
// Object instantiation — storage created on the stack
Tank armoredTank{"Advanced Periscope", "Heavy Gun Mantlet", ...};
// Second object — independent storage, independent state
Tank lightTank{"Simple Periscope", "Light Gun Mantlet", ...};
// armoredTank and lightTank each have their own copy of all 15 data members
// Modifying one does not affect the other
In modern C++, objects are typically created on the stack (automatic storage duration) using direct initialization. Objects requiring dynamic lifetime are managed through smart pointers —
std::unique_ptr<Tank> or
std::shared_ptr<Tank> — rather than raw
new and
delete.
Multiple Objects — Independent State
Every object of a class type has its own independent copy of all the class's data members. Creating a second Tank object does not share storage with the first — each object's periscope_, gun_mantlet_, and other data members are entirely independent. Member functions, by contrast, are shared — there is only one copy of displayTankInfo() in memory regardless of how many Tank objects exist. When a member function is called through an object, the compiler passes a hidden pointer to that specific object (the this pointer) so the function knows which object's data to access.
Access Specifiers — Controlling Visibility
Private, Public, and Protected Members
In C++, a class expands the struct concept by adding the ability to control access to its members through access specifiers. Three specifiers are available. private members are accessible only within the class itself — the default for class definitions. public members are accessible from anywhere — the default for struct definitions. protected members are accessible within the class and within any class that derives from it — covered in the inheritance modules. Access specifiers apply to all members that follow them until the next access specifier or the end of the class body.
Access Specifiers Enforce Encapsulation
This structure allows you to hide the internal workings of a class from outside interference and expose only what is necessary. Private data members cannot be read or written by code outside the class — the compiler enforces this at compile time, not runtime. Public member functions form the interface through which all interaction with the object must pass. The combination of private data and public behavior is the implementation of the encapsulation principle that lesson 4 established conceptually.
The Full Tank Class — A Realistic ADT
The following modernized Tank class demonstrates a complete, realistic class definition with 15 private data members covering all major tank components. The naming convention follows the module standard — snake_case with trailing underscore for data members, '\n' instead of std::endl for output, and member initializer list syntax for the constructor. This class models the same domain entities used in tank simulation games.
#include <iostream>
#include <string>
#include <string_view>
class Tank {
private:
std::string periscope_;
std::string gun_mantlet_;
std::string coaxial_gun_;
std::string main_gun_;
std::string drivers_optics_;
std::string drivers_hatch_;
std::string continuous_track_;
int machine_gun_ammo_ = 0;
std::string hatch_or_cupola_;
std::string gun_turret_;
std::string hull_;
std::string engine_air_intake_;
std::string engine_compartment_;
std::string link_;
std::string road_wheel_;
public:
Tank(std::string_view periscope,
std::string_view gun_mantlet,
std::string_view coaxial_gun,
std::string_view main_gun,
std::string_view drivers_optics,
std::string_view drivers_hatch,
std::string_view continuous_track,
int machine_gun_ammo,
std::string_view hatch_or_cupola,
std::string_view gun_turret,
std::string_view hull,
std::string_view engine_air_intake,
std::string_view engine_compartment,
std::string_view link,
std::string_view road_wheel)
: periscope_{periscope},
gun_mantlet_{gun_mantlet},
coaxial_gun_{coaxial_gun},
main_gun_{main_gun},
drivers_optics_{drivers_optics},
drivers_hatch_{drivers_hatch},
continuous_track_{continuous_track},
machine_gun_ammo_{machine_gun_ammo},
hatch_or_cupola_{hatch_or_cupola},
gun_turret_{gun_turret},
hull_{hull},
engine_air_intake_{engine_air_intake},
engine_compartment_{engine_compartment},
link_{link},
road_wheel_{road_wheel}
{}
void display_info() const {
std::cout << "Tank Details:\n";
std::cout << " Periscope : " << periscope_ << '\n';
std::cout << " Gun Mantlet : " << gun_mantlet_ << '\n';
std::cout << " Coaxial Gun : " << coaxial_gun_ << '\n';
std::cout << " Main Gun : " << main_gun_ << '\n';
std::cout << " Driver's Optics : " << drivers_optics_ << '\n';
std::cout << " Driver's Hatch : " << drivers_hatch_ << '\n';
std::cout << " Continuous Track : " << continuous_track_ << '\n';
std::cout << " MG Ammunition : " << machine_gun_ammo_ << '\n';
std::cout << " Hatch/Cupola : " << hatch_or_cupola_ << '\n';
std::cout << " Gun Turret : " << gun_turret_ << '\n';
std::cout << " Hull : " << hull_ << '\n';
std::cout << " Engine Air Intake : " << engine_air_intake_ << '\n';
std::cout << " Engine Compartment : " << engine_compartment_ << '\n';
std::cout << " Link : " << link_ << '\n';
std::cout << " Road Wheel : " << road_wheel_ << '\n';
}
};
int main() {
Tank armored_tank{
"Panoramic periscope", "Cast steel mantlet", "7.62mm coaxial",
"120mm smoothbore", "Night vision optics", "Reinforced hatch",
"Steel continuous track", 300, "Commander's cupola",
"Rotating turret", "Composite armor hull", "Rear air intake",
"V12 diesel", "Reinforced link", "High-durability wheel"
};
armored_tank.display_info();
}
Private Data Members — 15 Components
The 15 private data members cover every major subsystem of a tank: optical systems (periscope, drivers' optics), armament (gun mantlet, coaxial gun, main gun, ammunition), crew access (drivers' hatch, hatch or cupola), turret and hull structure (gun turret, hull), propulsion (continuous track, link, road wheel), and powerplant (engine air intake, engine compartment). All are private — client code cannot access or modify any component directly. The only way to interact with a Tank object is through its public interface.
Constructor — Initializing All Members
The constructor accepts 15 parameters — one for each data member — and uses a member initializer list to initialize all 15 before the constructor body executes. The parameters use std::string_view rather than const std::string& — std::string_view is a lightweight, non-owning reference to a string that avoids unnecessary copies when string literals are passed. The long parameter list is a design smell that would be addressed in a production system — C++20 designated initializers, a builder pattern, or a TankConfig data transfer object would make the construction call more readable. For a teaching example demonstrating what a complete class definition looks like, the explicit 15-parameter constructor makes every component visible and traceable.
The Tank Class in Game Development Context
Titles such as World of Tanks, Call to Arms: Panzer Elite, and WWII Tanks: Battlefield model exactly these components in their C++ class hierarchies. In a production game engine, the flat Tank class shown here would typically be decomposed into a hierarchy: a TankComponent base class or interface, with derived classes Periscope, GunMantlet, Hull, Turret, and DriveSystem — each with its own data, behavior, and damage model. The Tank class would then aggregate these component objects rather than storing raw strings. The 15 private data members in this lesson example are a simplified but structurally accurate representation of how such a system begins: identify the components of the entity, model each as private data, expose the interface that game logic needs (damage(), repair(), aim(), traverse()), and hide everything else. The class definition format introduced in this lesson — access specifiers, member functions, constructors — is the same format used at every level of that hierarchy.
In the next lesson, you will learn about the object-class concept — how objects and classes relate to the real-world entities they model.
