Classes
On this page
As our applications grow in features and complexity, we need to organize our code a little better. We cannot just throw everything inside ofApp as it quickly becomes unwieldy and hard to navigate.
We can use classes to separate our app functionality into different modules. A class is a structure that contains variables and functions as members.
- A class is the definition of this structure.
- An object is an instance of the class.
For example, in the following code snippet, ofPixels is the class and grabberPix is the object.
ofPixels grabberPix;
We have already been using classes throughout the course, as C++ is an object-oriented language. ofPixels, ofTexture, ofxRealSense2::Device are all classes. Even our application ofApp is a class and when we run our programs, we are instantiating an ofApp object and running it.
What we want to do now is try to create our own custom classes to hold our application code. While we can define our classes in any text file (as long as the compiler knows where to find it), there are conventions we should follow to keep things organized. Since we are writing code in OF, we will follow OF conventions:
- Classes should go in their own text files. One file for the header
.hand another for the implementation.cpp. - Either use a namespace or a prefix to identify what “group” this class belongs to. OF is slowly moving to namespaces, but for now all core classes are prefixed with “of”.
- The class name should be capitalized, but the prefix should be all lowercase.
For example, the ezFilter class would be defined in the files ezFilter.h and ezFilter.cpp.
The compiler needs to know where to find all the class files needed by the project. The easiest way to do this is to add them to the src folder of your project. We can also put them in a subfolder of src, in which case we should re-run the Project Generator. This will find all subfolders and add them to the compiler search paths.
Creating Classes
The process of creating OF classes is a little different in Visual Studio and Xcode, so we will go through both here.
Visual Studio
- Right-click on the
srcfolder in the Solution Explorer, then navigate to “Add” -> “New Item…”
- In the dialog that pops up, select “C++ File” from the list and name your class in the “Name” field. Make sure the “Location” is set to the
srcfolder in your project.
- Repeat these steps, this time selecting “Header File” from the list.

- We should then see new
.hand.cppfiles in the Solution Explorer. These are sometimes added to the root folder; if that’s the case just drag them into thesrcgroup.
The header file will have a single line of placeholder content in it.
// ezBall.h
#pragma once
As a reminder, #pragma once is an include guard command, and its purpose is to make sure the class is only defined once.
- One way to understand this is to think that the compiler holds a table of contents of every class and its functions available in the application.
- Whenever it sees a header file, it adds its contents to this table of contents.
- In C++, we use an
#includedirective whenever we want to use a class defined in a different file. Whenever we use#include, the contents of that file are added to our table of contents. - If we want to reuse the same class in many different places, we would include it again, and its contents would be repeated in our table of contents, causing a conflict!
- This is where include guards (or header guards) come in; they ensure that the contents of the header file are only included once.
Xcode
- Right-click on the
srcfolder in the Project Navigator, then select"New File…"
- Select “C++ File” from the list.

- Name your class in the next dialog, and make sure “Also create header file” is selected.

- Ensure that the files will be saved to the
srcfolder, and added to thesrcgroup in the next dialog.
- We should then see new
.hppand.cppfiles in the Project Navigator.
The header looks quite different from the VS version.
// ezBall.hpp
#ifndef ezBall_hpp
#define ezBall_hpp
#include <stdio.h>
#endif // ezBall_hpp
// ezBall.cpp
#include "ezBall.hpp"
.hpp is an alternate extension for C++ header files. It is not used in OF, so we will change it to .h to follow convention.
- This can be done directly in the Xcode Project Navigator.
- Note that we will also need to update the references to this file, like the
#includedirective in the.cppfile.
The placeholder uses a different type of include guard. Lines that begin with # are called compiler directives. We can think of them as a programming language for the compiler.
#ifndefstands for “if not defined”. If this condition is true, then everything between this line and#endifwill be included.#definedefines a new variable.- Those lines can therefore be interpreted as: “If the variable
ezBall_hppdoes not exist, create it and include everything else in the file”. - We would then define the class above the
#endif, to make sure it only gets included once.
For consistency’s sake, we will replace this with the #pragma once header guard.
We can also remove the call to #include <stdio.h> as we do not need to explicitly include it.
Class Members
We should now include a basic class definition in our files:
- The header will define and name the class, and include sections for all
publicandprivatemember variables and functions. - The implementation will start with an
#includedirective, telling it where the class definiton is. This is simply the header file.
// ezBall.h
#pragma once
class ezBall
{
public:
private:
};
// ezBall.cpp
#include "ezBall.h"
Let’s add basic functionality to our ezBall.
- We will use similar terminology to OF, adding a
setup()function to initialize the object, and adraw()function to render it. - We will add class variables for position, mass (which we’ll use as radius), and color.
- We will include
ofMain.hin our header file to give us access to openFrameworks objects and functions.
// ezBall.h
#pragma once
#include "ofMain.h"
class ezBall
{
public:
void setup(int x, int y);
void draw();
private:
glm::vec2 pos;
float mass;
ofColor color;
};
// ezBall.cpp
#include "ezBall.h"
void ezBall::setup(int x, int y)
{
pos = glm::vec2(x, y);
mass = ofRandom(10, 30);
color = ofColor(ofRandom(127, 255), ofRandom(127, 255), ofRandom(127, 255));
}
void ezBall::draw()
{
ofSetColor(color);
ofDrawCircle(pos, mass);
}
We can now include the ezBall header in our main app, and instantiate and use ezBall objects, calling any public members defined in the class.
// ofApp.h
#pragma once
#include "ofMain.h"
#include "ezBall.h"
class ofApp : public ofBaseApp
{
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void addBall(int x, int y);
vector<ezBall> balls;
};
// ofApp.cpp
#include "ofApp.h"
void ofApp::setup()
{
ofBackground(0);
}
void ofApp::update()
{
}
void ofApp::draw()
{
for (int i = 0; i < balls.size(); i++)
{
balls[i].draw();
}
// OR
//for (auto b : balls)
//{
// b.draw();
//}
}
void ofApp::addBall(int x, int y)
{
// Add a new ezBall.
balls.push_back(ezBall());
// Setup the last added ezBall.
balls.back().setup(x, y);
}
void ofApp::keyPressed(int key)
{
if (key == ' ')
{
balls.clear();
}
}
void ofApp::mouseDragged(int x, int y, int button)
{
addBall(x, y);
}
void ofApp::mousePressed(int x, int y, int button)
{
addBall(x, y);
}
Let’s add a bit of physics to make the ezBall more dynamic. We will add a new update() method to pass a force to the ball, and calculate its position every frame.
// ezBall.h
#pragma once
#include "ofMain.h"
class ezBall
{
public:
void setup(int x, int y);
void update(glm::vec2 force);
void draw();
private:
glm::vec2 pos;
glm::vec2 vel;
glm::vec2 acc;
float mass;
ofColor color;
};
// ezBall.cpp
#include "ezBall.h"
void ezBall::setup(int x, int y)
{
pos = glm::vec2(x, y);
mass = ofRandom(10, 30);
color = ofColor(ofRandom(127, 255), ofRandom(127, 255), ofRandom(127, 255));
}
void ezBall::update(glm::vec2 force)
{
// Add force.
acc += force / mass;
if (glm::length(vel) > 0)
{
// Add friction.
glm::vec2 friction = glm::normalize(vel * -1) * 0.01f;
acc += friction;
}
// Apply acceleration, then reset it!
vel += acc;
acc = glm::vec2(0.0f);
// Move object.
pos += vel;
// Bounce off walls, taking radius into consideration.
if (pos.x - mass < 0 || pos.x + mass > ofGetWidth())
{
pos.x = ofClamp(pos.x, mass, ofGetWidth() - mass);
vel.x *= -1;
}
if (pos.y - mass < 0 || pos.y + mass > ofGetHeight())
{
pos.y = ofClamp(pos.y, mass, ofGetHeight() - mass);
vel.y *= -1;
}
}
void ezBall::draw()
{
ofSetColor(color);
ofDrawCircle(pos, mass);
}
As we add more functionality to the class, this becomes available to any object using ezBall objects.
// ofApp.cpp
#include "ofApp.h"
void ofApp::setup()
{
ofBackground(0);
}
void ofApp::update()
{
glm::vec2 gravity = glm::vec2(0, 9.8f);
for (int i = 0; i < balls.size(); i++)
{
balls[i].update(gravity);
}
// OR
//for (auto b : balls)
//{
// b.update(gravity);
//}
}
void ofApp::draw()
{
for (int i = 0; i < balls.size(); i++)
{
balls[i].draw();
}
// OR
//for (auto b : balls)
//{
// b.draw();
//}
}
void ofApp::addBall(int x, int y)
{
// Add a new ezBall.
balls.push_back(ezBall());
// Setup the last added ezBall.
balls.back().setup(x, y);
}
void ofApp::keyPressed(int key)
{
if (key == ' ')
{
balls.clear();
}
}
void ofApp::mouseDragged(int x, int y, int button)
{
addBall(x, y);
}
void ofApp::mousePressed(int x, int y, int button)
{
addBall(x, y);
}
