Classes & Objects
So far your data has lived in separate variables: a name here, a roll number there, marks somewhere else. Real things have several pieces of data that belong together, and rules about how that data may change. A class groups data and the functions that work on it into one new type. Each variable of that type is an object.
Classes are the heart of object-oriented programming (OOP) in C++. In this lesson you will create simple data types with struct, protect data with private members in a class, write member functions, and split a class across header and source files.
struct: grouping related data
The simplest class is a struct whose members are all public. Each member has a type and a name, and can have a default value.
#include <print>
#include <string>
struct Student {
std::string name;
int roll = 0;
double percentage = 0.0;
};
int main() {
Student a{"Aarav", 12, 86.5};
Student b{.name = "Isha", .roll = 7, .percentage = 91.0}; // C++20
b.percentage += 1.5;
Student c = a; // copies every member
c.name = "Aarav's copy";
std::println("{} (roll {}) scored {}%", a.name, a.roll, a.percentage);
std::println("{} (roll {}) scored {}%", b.name, b.roll, b.percentage);
std::println("{}", c.name);
}Aarav (roll 12) scored 86.5% Isha (roll 7) scored 92.5% Aarav's copy
You reach members with a dot: b.percentage. The second object uses designated initializers (C++20), which name each member and make long initialisations readable. Notice that c = a makes a full, independent copy: changing c.name does not touch a. C++ objects behave like values, unlike Java or Python objects, which are shared through references.
class: data with rules
A bank balance should never be changed directly by any code that feels like it; deposits must be positive, and withdrawals must not exceed the balance. A class lets you hide the data behind access specifiers:
public:members can be used by any code.private:members can be used only by the class's own member functions.
#include <print>
class BankAccount {
public:
void deposit(long long amount) {
if (amount <= 0) {
std::println("Deposit must be positive");
return;
}
balance_ += amount;
}
bool withdraw(long long amount) {
if (amount > balance_) {
return false;
}
balance_ -= amount;
return true;
}
long long balance() const { return balance_; }
private:
long long balance_ = 0; // whole rupees
};
int main() {
BankAccount account;
account.deposit(5000);
account.deposit(-20);
bool ok = account.withdraw(1200);
bool too_much = account.withdraw(10000);
std::println("Withdraw 1200: {}, withdraw 10000: {}", ok, too_much);
std::println("Balance: Rs. {}", account.balance());
// account.balance_ = 1'000'000; // error: 'balance_' is private
}Deposit must be positive Withdraw 1200: true, withdraw 10000: false Balance: Rs. 3800
Hiding data and allowing changes only through checked member functions is called encapsulation. The rule "the balance is never negative" is called an invariant, and the class guarantees it. Many codebases mark private members with a trailing underscore, as in balance_, so they are easy to recognise.
The only technical difference between struct and class is the default access: struct members are public by default, class members private. By convention, use struct for simple bundles of data and class when there are rules to protect.
Member functions and const
Functions declared inside a class are member functions (also called methods). Inside them, you can use the members directly, and the hidden pointer this points to the current object.
A member function marked const after its brackets promises not to change the object. Only const member functions can be called on a const object, so mark every function that only reads.
#include <print>
struct Rectangle {
double width = 0;
double height = 0;
double area() const; // declared here, defined below
void scale(double factor);
};
double Rectangle::area() const {
return width * height;
}
void Rectangle::scale(double factor) {
this->width *= factor; // same as width *= factor
height *= factor;
}
int main() {
Rectangle room{4, 2.5};
room.scale(2);
std::println("Room: {} x {} = {} sq m", room.width, room.height, room.area());
const Rectangle plot{10, 20};
std::println("Plot area: {} sq m", plot.area());
// plot.scale(2); // error: plot is const
}Room: 8 x 5 = 40 sq m Plot area: 200 sq m
When a member function is defined outside the class, its name gets the class prefix: Rectangle::area. The :: is the scope resolution operator.
Header and source files
In real projects each class usually has two files. The header (.h or .hpp) contains the class definition, so other files can #include it. The source file (.cpp) contains the member function bodies.
// rectangle.h
#pragma once
struct Rectangle {
double width = 0;
double height = 0;
double area() const;
};// rectangle.cpp
#include "rectangle.h"
double Rectangle::area() const {
return width * height;
}// main.cpp
#include <print>
#include "rectangle.h"
int main() {
Rectangle hall{12, 9};
std::println("Hall area: {} sq m", hall.area());
}g++ -std=c++23 -Wall main.cpp rectangle.cpp -o hall
./hallHall area: 108 sq m
#pragma once stops a header from being included twice in the same file. Your own headers use double quotes ("rectangle.h"), standard headers use angle brackets. Both .cpp files are compiled and then linked into one program. C++20 also offers modules as a modern alternative to headers; see Namespaces & Modules.
Common mistakes
- Forgetting the semicolon after the class.
struct Student { ... }must end with};. The error often appears on a later line. - Making everything public. Then any code can break your invariants. Keep data private when rules apply.
- Missing
conston reading functions. Without it, you cannot call the function on aconstobject or through aconst¶meter. - Writing a getter and setter for every member. If a setter accepts any value, the member might as well be public. Provide operations that make sense, like
deposit, instead.
Key points
- A class bundles data members and member functions into a new type; objects are values of that type.
structmembers are public by default,classmembers private; usepublic:andprivate:to control access.- Encapsulation keeps invariants safe by changing data only through checked member functions.
- Mark member functions that only read with
const; define functions outside the class asClassName::function. - Put class definitions in headers with
#pragma onceand member bodies in.cppfiles.
Exercise
Create a class CricketPlayer with private members name_ (string), runs_ and balls_ (both int, starting at 0). Add a public function set_name, a function add_innings(int runs, int balls) that ignores negative values, and const functions name() and strike_rate() (runs per 100 balls, 0 if no balls). Create Rohan, add innings of 45 off 30 and 82 off 61, and print his strike rate with two decimals.
Show solution
#include <print>
#include <string>
class CricketPlayer {
public:
void set_name(const std::string& name) { name_ = name; }
void add_innings(int runs, int balls) {
if (runs < 0 || balls < 0) {
return;
}
runs_ += runs;
balls_ += balls;
}
const std::string& name() const { return name_; }
double strike_rate() const {
if (balls_ == 0) {
return 0.0;
}
return runs_ * 100.0 / balls_;
}
private:
std::string name_;
int runs_ = 0;
int balls_ = 0;
};
int main() {
CricketPlayer rohan;
rohan.set_name("Rohan");
rohan.add_innings(45, 30);
rohan.add_innings(82, 61);
std::println("{}: strike rate {:.2f}", rohan.name(), rohan.strike_rate());
}Rohan: strike rate 139.56
Rohan has 127 runs from 91 balls, and 127 × 100 / 91 is about 139.56. Multiplying by 100.0 makes the calculation use decimals. Setting the name in a separate step is clumsy; the next lesson replaces it with a constructor.
Quick Quiz
1. What is the default access of members in a class?
class members are private unless you write public:; struct members are public by default.
2. Why mark a member function const?
const member functions can be called on const objects and through const& parameters.
3. After Student c = a; c.name = "X";, what is a.name?
Assigning one object to another copies all members; the two objects are independent.
4. How do you define the function area of struct Rectangle outside the struct?
The class name and :: tell the compiler which class the function belongs to.
Learn C++ with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
