Recommended resources
The freely available resources this lesson will direct you to.
Welcome to your first lesson in the "Modern C++ for Graphics Programming" module. Our goal today is to build a solid foundation in Object-Oriented Programming (OOP) by learning how to define and use C++ classes. This is the first step in our journey to master the C++ skills necessary for advanced graphics programming.
You've mentioned that you're a beginner in OOP but comfortable with the basics of C++. This lesson is designed to formalize your understanding of classes, which are the cornerstone of organizing large software projects like rendering engines. In graphics, almost everything is represented as an object: a 3D model, a light source, a camera, or even mathematical concepts like vectors and matrices. By the end of this lesson, you will be able to define your own classes, complete with the data they hold and the functions they can perform.
Why We Need Classes
Before diving into syntax, let's understand why classes are so important. Imagine you're programming a simple 2D game. You need to keep track of a player's position and speed. Without classes, you might create a set of loose variables:
int playerX;
int playerY;
int playerSpeed;
This works for one player, but it's disorganized. What if you need two players? The number of variables doubles, and it quickly becomes messy to manage. Furthermore, any function that moves the player needs to accept all these variables as parameters.
Classes solve this by letting us create a new type—a blueprint—that groups related data and functionality together.
To see this problem and its solution explained visually, please watch the beginning of the following video from The Cherno's C++ series.
This video provides a clear, practical introduction to why classes are necessary in C++.
Watch the segment from the beginning up to the introduction of the class keyword. Focus on the issues that arise from using separate variables to represent a single entity. You'll see how this approach becomes difficult to manage, which sets the stage for the class-based solution. The timestamps are from the start.
Defining a Class
A class is a user-defined blueprint from which objects are created. It combines data members (variables) and member functions (methods) into a single unit. An object is a specific instance of a class, with its own set of data.
Let's look at the syntax for creating a class and instantiating an object.
The same video continues by demonstrating the basic syntax for defining a class, creating an object, and accessing its members.
Continue watching from where you left off. Pay close attention to the class keyword, the placement of member variables, and the use of the public access specifier. The video explains why members are not accessible by default and how to fix it. The relevant part is from class syntax.
As you saw, members of a class are private by default. This is a key concept in OOP called encapsulation.
publicmembers can be accessed from anywhere outside the class.privatemembers can only be accessed by member functions within the class itself.
Encapsulation helps protect an object's internal state from accidental modification and keeps the class's interface clean and predictable. We'll stick to public for now for simplicity, but private is essential for robust design.
In C++, you'll also frequently encounter the struct keyword. It is nearly identical to class, with one main difference: its members are public by default. By convention, structs are often used for simple data aggregates that don't have complex behavior, which is very common in graphics programming for types like vectors or colors.
Member Functions (Methods)
Classes are more than just data containers; they also define behavior through member functions, also known as methods. These are functions that are part of the class and can directly operate on its member variables.
This final clip from the video demonstrates how to move a function into a class, turning it into a method.
Watch this segment to see how a standalone move function is integrated into the Player class. Notice how the method call becomes more intuitive (player.move()) and how the function no longer needs the player object passed as a parameter. Watch from here.
This is a much cleaner design. The logic for moving a player is now bundled with the player's data, right where it belongs.
Constructors and Destructors
When an object is created, how do we ensure its member variables are initialized to sensible default values? And when it's destroyed, how do we clean up any resources it was using? This is the job of two special member functions: the constructor and the destructor.
Constructors: Initializing Objects
A constructor is a special method that is automatically called when an object of a class is created. Its primary job is to initialize the member variables.
In this video, The Cherno explains the role of constructors.
Watch the first part of this video, which explains the problem of uninitialized variables and shows how a constructor solves it. You'll see the syntax for a default constructor (one that takes no arguments). Watch from the beginning.
Key characteristics of a constructor:
- It has the exact same name as the class.
- It has no return type, not even
void. - It can be overloaded, meaning you can have multiple constructors with different parameters.
A constructor with no parameters is called the default constructor. A class can also have parameterized constructors that accept arguments to initialize the object with specific values.
Let's look at a very practical example from graphics programming: a 2D vector class.
Game Math 101, Writing your Own 2D Math in C++
This article from Randy Gaul's blog demonstrates building a 2D math library from scratch. We'll look at how a v2 struct is defined.
In the article, find the section "Positions (points) and Vectors". Focus on the definition of the v2 struct: struct v2 Notice it has two constructors: v2() is the default constructor, and v2(float x, float y) is a parameterized constructor. This allows you to create a vector in two ways: v2 my_vec1; (uses the default constructor) v2 my_vec2(3.0f, 4.0f); (uses the parameterized constructor)
Destructors: Cleaning Up
Just as a constructor is called when an object is created, a destructor is called when an object is destroyed (e.g., when it goes out of scope or is explicitly deleted). Its purpose is to release any resources the object acquired during its lifetime.
The most common use for a destructor is to deallocate memory that was dynamically allocated by the object. For simple classes like our v2 example that don't manage dynamic memory, the default destructor provided by the compiler is sufficient. However, understanding their purpose is crucial for more advanced C++.
Templated Classes & Vector Implementation
These lecture notes from Rensselaer Polytechnic Institute cover C++ vector implementation. We will just look at the definition of a destructor.
Find section 7.14 in the document. Read the three bullet points that describe the destructor, starting from this description. Note its syntax (~ClassName()) and its role in managing dynamic memory.
A Complete Example: Vec3
Let's tie everything together by creating a Vec3 class for 3D vectors, a type you will use constantly in graphics programming.
Here is a complete program. Read through the code and the comments to see how member variables, constructors, a destructor, and a member function all work together.
#include <iostream>
#include <cmath> // For std::sqrt
// A class to represent a 3D vector.
class Vec3 {
public:
// Member variables
float x, y, z;
// Default constructor: initializes to a zero vector.
Vec3() {
x = 0.0f;
y = 0.0f;
z = 0.0f;
std::cout << "Vec3 default constructor called. Vector is (" << x << ", " << y << ", " << z << ")" << std::endl;
}
// Parameterized constructor
Vec3(float x_val, float y_val, float z_val) {
x = x_val;
y = y_val;
z = z_val;
std::cout << "Vec3 parameterized constructor called. Vector is (" << x << ", " << y << ", " << z << ")" << std::endl;
}
// Destructor
~Vec3() {
std::cout << "Vec3 destructor called for vector (" << x << ", " << y << ", " << z << ")" << std::endl;
}
// Member function to calculate the length (magnitude) of the vector.
// The 'const' keyword promises that this function will not modify the object's state.
float length() const {
return std::sqrt(x*x + y*y + z*z);
}
};
int main() {
std::cout << "--- Creating vectors ---" << std::endl;
// Create a Vec3 object v1 using the default constructor
Vec3 v1;
// Create a Vec3 object v2 using the parameterized constructor
Vec3 v2(3.0f, 4.0f, 0.0f);
std::cout << "\n--- Using vectors ---" << std::endl;
std::cout << "Length of v2 is: " << v2.length() << std::endl;
std::cout << "\n--- Exiting main scope ---" << std::endl;
return 0; // v2 and v1 are destroyed here, in reverse order of creation.
}
When you compile and run this code, pay attention to the output. You will see the constructor and destructor messages being printed, showing you the exact lifecycle of the v1 and v2 objects.
Conclusion
In this lesson, we established the fundamental concepts of C++ classes. You learned how to define a class to bundle data and behavior, how to use constructors to initialize objects, and the role of destructors in cleanup. This organizational principle is the heart of OOP and is indispensable for writing the clean, modular, and scalable code required for graphics applications.
Key Takeaways:
- A class (or
struct) acts as a blueprint for creating objects, grouping member variables and member functions. - Encapsulation (
publicvs.private) controls access to an object's members, protecting its internal state. - A constructor is a special method automatically called on object creation to handle initialization.
- A destructor is a special method automatically called on object destruction, primarily for resource cleanup.
- Member functions define an object's behavior and operate on its data.
In our next lesson, we will explore inheritance, a powerful mechanism that allows one class to derive properties and behaviors from another. This will enable us to build hierarchies of related objects, such as a general Shape class with specialized Sphere and Cube children—a common and powerful pattern in rendering.