Classes and Objects
A class is a blueprint that defines the data (member variables) and behaviour (member functions/methods) of objects. An object is an instance of a class — a concrete thing built from that blueprint. Classes are the foundation of object-oriented programming in C++.
Defining a Class
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private: // Access specifier — hidden from outside
string owner;
double balance;
int accountNumber;
public: // Accessible from outside
// Constructor — initialises the object
BankAccount(const string& ownerName, double initialBalance, int accNum) {
owner = ownerName;
balance = initialBalance;
accountNumber = accNum;
}
// Member functions (methods)
void deposit(double amount) {
if (amount > 0) balance += amount;
}
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
double getBalance() const { // const — does not modify the object
return balance;
}
void print() const {
cout << "Account #" << accountNumber
<< " (" << owner << "): ₹" << balance << "\n";
}
};
int main() {
BankAccount acc("Alice", 5000.0, 1001);
acc.deposit(2500.0);
acc.withdraw(1000.0);
acc.print();
// Account #1001 (Alice): ₹6500
return 0;
}Access Specifiers
| Specifier | Accessible from |
|---|---|
public | Anywhere — the class's interface |
private | Only from within the class (default for class) |
protected | Within the class and its subclasses |
struct in C++ is identical to class except all members are public by default. Use struct for plain data containers; use class for types with invariants (rules about the data).
The this Pointer
Inside any member function, this is a pointer to the current object:
#include <iostream>
#include <string>
using namespace std;
class Counter {
int count;
string name;
public:
Counter(int count, const string& name) {
// Parameter name shadows member name — use 'this->' to distinguish
this->count = count;
this->name = name;
}
// Method chaining — return *this to allow c.inc().inc().inc()
Counter& increment() {
count++;
return *this;
}
Counter& add(int n) {
count += n;
return *this;
}
void print() const {
cout << name << ": " << count << "\n";
}
};
int main() {
Counter c(0, "clicks");
c.increment().increment().add(5).increment();
c.print(); // clicks: 8
return 0;
}Const Member Functions
A const member function promises not to modify the object. It can be called on const objects:
class Rectangle {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const { return width * height; }
double perimeter() const { return 2 * (width + height); }
void scale(double factor) { // Non-const — modifies the object
width *= factor;
height *= factor;
}
};
void printInfo(const Rectangle& r) {
cout << "Area: " << r.area() << "\n"; // OK — const method
// r.scale(2.0); // ERROR — cannot call non-const method on const ref
}Static Members
static members belong to the class, not to individual objects:
#include <iostream>
#include <string>
using namespace std;
class Employee {
static int nextId; // Shared across all Employee objects
int id;
string name;
double salary;
public:
Employee(const string& name, double salary)
: id(nextId++), name(name), salary(salary) {}
static int getCount() { return nextId - 1; } // Static method
void print() const {
cout << "ID " << id << ": " << name << " (₹" << salary << ")\n";
}
};
// Static member must be defined outside the class
int Employee::nextId = 1;
int main() {
Employee e1("Alice", 75000);
Employee e2("Bob", 65000);
Employee e3("Charlie", 80000);
e1.print(); // ID 1: Alice (₹75000)
e2.print(); // ID 2: Bob (₹65000)
e3.print(); // ID 3: Charlie (₹80000)
cout << "Total employees: " << Employee::getCount() << "\n"; // 3
return 0;
}Getters and Setters
Encapsulation means hiding data and providing controlled access:
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;
class Temperature {
double celsius; // Private — in Celsius internally
public:
Temperature(double c = 0.0) {
setCelsius(c); // Use setter to validate on construction too
}
// Getter
double getCelsius() const { return celsius; }
double getFahrenheit() const { return celsius * 9.0 / 5.0 + 32; }
double getKelvin() const { return celsius + 273.15; }
// Setter with validation
void setCelsius(double c) {
if (c < -273.15) {
throw invalid_argument("Temperature below absolute zero!");
}
celsius = c;
}
void setFahrenheit(double f) {
setCelsius((f - 32) * 5.0 / 9.0);
}
};
int main() {
Temperature t(100.0);
cout << t.getCelsius() << "°C\n"; // 100°C
cout << t.getFahrenheit() << "°F\n"; // 212°F
cout << t.getKelvin() << "K\n"; // 373.15K
t.setFahrenheit(32.0);
cout << t.getCelsius() << "°C\n"; // 0°C
try {
t.setCelsius(-300); // Throws!
} catch (const invalid_argument& e) {
cout << "Error: " << e.what() << "\n";
}
return 0;
}Separating Declaration from Implementation
In real projects, class declarations go in .h (header) files and definitions go in .cpp files:
// rectangle.h
#pragma once
class Rectangle {
double width, height;
public:
Rectangle(double w, double h);
double area() const;
double perimeter() const;
void scale(double factor);
};
// rectangle.cpp
#include "rectangle.h"
Rectangle::Rectangle(double w, double h) : width(w), height(h) {}
double Rectangle::area() const { return width * height; }
double Rectangle::perimeter() const { return 2 * (width + height); }
void Rectangle::scale(double factor) {
width *= factor;
height *= factor;
}Practical Example: Student Grade Manager
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include <algorithm>
using namespace std;
class Student {
string name;
vector<double> grades;
public:
Student(const string& name) : name(name) {}
void addGrade(double grade) {
if (grade >= 0 && grade <= 100)
grades.push_back(grade);
}
double average() const {
if (grades.empty()) return 0.0;
return accumulate(grades.begin(), grades.end(), 0.0) / grades.size();
}
double highest() const {
if (grades.empty()) return 0.0;
return *max_element(grades.begin(), grades.end());
}
double lowest() const {
if (grades.empty()) return 0.0;
return *min_element(grades.begin(), grades.end());
}
string letterGrade() const {
double avg = average();
if (avg >= 90) return "A";
if (avg >= 80) return "B";
if (avg >= 70) return "C";
if (avg >= 60) return "D";
return "F";
}
const string& getName() const { return name; }
void printReport() const {
cout << "Student: " << name << "\n";
cout << " Grades: ";
for (double g : grades) cout << g << " ";
cout << "\n";
cout << " Average: " << average() << "\n";
cout << " Highest: " << highest() << "\n";
cout << " Lowest: " << lowest() << "\n";
cout << " Grade: " << letterGrade() << "\n";
}
};
class Classroom {
string className;
vector<Student> students;
public:
Classroom(const string& name) : className(name) {}
void addStudent(Student s) {
students.push_back(move(s));
}
double classAverage() const {
if (students.empty()) return 0.0;
double total = 0.0;
for (const auto& s : students) total += s.average();
return total / students.size();
}
void printReport() const {
cout << "=== " << className << " ===\n\n";
for (const auto& s : students) {
s.printReport();
cout << "\n";
}
cout << "Class average: " << classAverage() << "\n";
}
};
int main() {
Classroom cls("C++ Fundamentals");
Student alice("Alice");
alice.addGrade(92); alice.addGrade(88); alice.addGrade(95);
Student bob("Bob");
bob.addGrade(75); bob.addGrade(82); bob.addGrade(78);
Student charlie("Charlie");
charlie.addGrade(60); charlie.addGrade(55); charlie.addGrade(70);
cls.addStudent(alice);
cls.addStudent(bob);
cls.addStudent(charlie);
cls.printReport();
// === C++ Fundamentals ===
//
// Student: Alice
// Grades: 92 88 95
// Average: 91.6667
// Grade: A
// ...
// Class average: 77.2222
return 0;
}Summary
- A
classbundles data (member variables) and behaviour (member functions) into one type privatemembers are hidden — only accessible from within the class;publicmembers form the interfaceconstmember functions cannot modify the object — mark all read-only methods asconst- The
thispointer refers to the current object; return*thisto enable method chaining staticmembers belong to the class itself, not to individual objects; access them viaClassName::member- Separate class declarations (
.h) from definitions (.cpp) for maintainable, reusable code - Use getters/setters to validate data before accepting it — this is the key benefit of encapsulation
Next, we'll learn about constructors and destructors — how objects are built and cleaned up in C++.