Paramclasses Academy - Blog

Tutorials Details

What is Classes and Objects in C++

What is Classes and Objects in C++

October 15, 2026

Classes and objects are the foundation of object-oriented programming (OOP) in C++. They allow you to combine data and the functions that operate on that data into a single unit.

In Module 10, you learned how struct groups related information. Now, you’ll learn how classes can also control access to data, protect an object's internal state, and organize larger programs.

1. Learning objectives

By the end of this module, you will be able to:

  • Define a class and create objects.

  • Understand the difference between a class and an object.

  • Use public, private, and protected access specifiers.

  • Write member functions inside and outside a class.

  • Create constructors and use constructor overloading.

  • Use the this pointer.

  • Apply encapsulation with getters and setters.

  • Work with multiple objects and static members.

  • Build a simple bank account management program.

Estimated time

75–100 minutes

Prerequisites

Structures, functions, variables, and basic C++ syntax

2. What is a class?

A class is a user-defined type that describes the data and behavior its objects can have.

An object is an instance of a class.

For example, imagine a class called Student. It may have data such as a student's name and marks, along with functions to display the student’s details or update the marks.

Class: Student

Data members

  • name

  • marks

    Member functions

  • display()

  • updateMarks()

Creates instances

Object s1

Aman, 85

Object s2

Priya, 92

Both objects share the same class definition, but each normally has its own non-static member data.

A useful analogy is a building blueprint: the class is the blueprint, while objects are the individual buildings constructed from it.

3. Class syntax in C++

The general syntax is:

class ClassName {
private:
    // Data members and helper functions

public:
    // Functions and operations accessible to users
};

Important rules:

  • Use the class keyword to define a class.

  • The class body is enclosed in braces {}.

  • A class definition ends with a semicolon.

  • private members can be accessed directly only from permitted class contexts, such as the class's own member functions and friends.

  • public members form the interface that users of the class can access.

Example 1 — Your first class


#include <iostream>
#include <string>
using namespace std;

class Student {
public:
    string name;
    int marks;

    void display() {
        cout << "Name: " << name << endl;
        cout << "Marks: " << marks << endl;
    }
};

int main() {
    Student s1;

    s1.name = "Aman";
    s1.marks = 85;

    s1.display();

    return 0;
}

Output

Name: Aman
Marks: 85

Explanation:

  1. class Student defines the class.

  2. name and marks are data members.

  3. display() is a member function.

  4. Student s1; creates an object.

  5. s1.display() calls the function on that object.

This is a simple introductory example. In a well-encapsulated class, data is often private and accessed through public functions.

4. Objects and member access

An object is created using the class name followed by a variable name.

Student s1;
Student s2;

When members are public, use the dot operator (.) to access them through an object:

s1.name = "Aman";
s2.name = "Priya";

Each object has its own non-static data members. Updating s1.name does not change s2.name.

Example 2 — Multiple objects


#include <iostream>
#include <string>
using namespace std;

class Student {
public:
    string name;
    int marks;

    void display() {
        cout << name << " scored " << marks << endl;
    }
};

int main() {
    Student s1;
    Student s2;

    s1.name = "Aman";
    s1.marks = 85;

    s2.name = "Priya";
    s2.marks = 92;

    s1.display();
    s2.display();

    return 0;
}

Output

Aman scored 85
Priya scored 92

5. Access specifiers: public, private, and protected

Access specifiers control which parts of a program can access class members.

public

Members can be accessed from code that has access to the object.

private

Members can be accessed directly by the class's own member functions and friends, but not by ordinary external code.

protected

Members can be accessed by the class's member functions and friends, and by derived classes in permitted contexts. Ordinary external code cannot access them directly.

Important default rule: Members of a class are private by default. Members of a struct are public by default.

Example 3 — Private data


#include <iostream>
using namespace std;

class BankAccount {
private:
    double balance = 1000.0;

public:
    void displayBalance() {
        cout << "Balance: " << balance << endl;
    }
};

int main() {
    BankAccount account;

    account.displayBalance();

    // account.balance = 5000; // Error: private member

    return 0;
}

Output

Balance: 1000

The object can call displayBalance() because it is public. It cannot directly access balance because that member is private.

This helps protect an object's internal state from unintended changes.

6. Encapsulation: protecting data

Encapsulation means combining data and the operations that work on it while controlling how the data can be accessed.

For example, a bank account should not allow arbitrary code to set its balance to an invalid value. Instead, public functions can enforce rules.

Example 4 — Getters and setters

A getter reads a value. A setter updates a value, often after validating it.


#include <iostream>
#include <string>
using namespace std;

class Student {
private:
    string name;
    int marks = 0;

public:
    void setName(const string& studentName) {
        name = studentName;
    }

    void setMarks(int studentMarks) {
        if (studentMarks >= 0 && studentMarks <= 100) {
            marks = studentMarks;
        } else {
            cout << "Invalid marks!" << endl;
        }
    }

    string getName() const {
        return name;
    }

    int getMarks() const {
        return marks;
    }
};

int main() {
    Student s1;

    s1.setName("Aman");
    s1.setMarks(88);

    cout << "Name: " << s1.getName() << endl;
    cout << "Marks: " << s1.getMarks() << endl;

    s1.setMarks(150);

    cout << "Final marks: " << s1.getMarks() << endl;

    return 0;
}

Output

Name: Aman
Marks: 88
Invalid marks!
Final marks: 88

Why is this better?

  • marks is private.

  • setMarks() accepts only values between 0 and 100.

  • An invalid update is rejected.

  • getMarks() const reads the value without modifying the object.

Notice the const after a member function's parentheses. It promises that the function will not modify the object's ordinary non-mutable data members.

7. Member functions inside and outside a class

Member functions can be defined inside the class body or declared inside and defined outside the class.

A. Define the function inside the class

class Calculator {
public:
    int add(int a, int b) {
        return a + b;
    }
};

B. Define the function outside the class

Use the scope resolution operator (::) to indicate which class the function belongs to.


#include <iostream>
using namespace std;

class Calculator {
public:
    int add(int a, int b);
};

int Calculator::add(int a, int b) {
    return a + b;
}

int main() {
    Calculator calc;

    cout << calc.add(10, 20) << endl;

    return 0;
}

Output

30

In this example:

  • int add(int a, int b); declares the member function.

  • Calculator::add(...) defines that function outside the class.

  • :: associates the definition with Calculator.

Separating declarations and definitions can make larger projects easier to organize.

8. Constructors

A constructor is a special member function that initializes an object when it is created.

Constructor rules:

  • Its name matches the class name.

  • It has no return type—not even void.

  • It runs automatically when an object is initialized.

  • A class can have multiple constructors with different parameter lists.

Example 5 — A parameterized constructor


#include <iostream>
#include <string>
using namespace std;

class Student {
private:
    string name;
    int marks;

public:
    Student(string studentName, int studentMarks) {
        name = studentName;
        marks = studentMarks;
    }

    void display() const {
        cout << name << " scored " << marks << endl;
    }
};

int main() {
    Student s1("Aman", 85);
    Student s2("Priya", 92);

    s1.display();
    s2.display();

    return 0;
}

Output

Aman scored 85
Priya scored 92

The constructor initializes each object's data when it is created.

Constructor initializer lists

In modern C++, initializer lists are usually preferred for initializing data members.

Student(string studentName, int studentMarks)
    : name(studentName), marks(studentMarks) {
}

The part after : is called the member initializer list. It initializes the members directly, rather than default-initializing them and assigning new values afterward.

Initializer lists are required for certain members, such as reference members and const data members, and are also important for class-type members.

9. Types of constructors

A. Default constructor

A default constructor can be called without arguments.

class Box {
private:
    int length;

public:
    Box() : length(1) {}

    void display() const {
        cout << length << endl;
    }
};

Usage:

Box b;
b.display();

Output:

1

If you declare your own constructor with parameters, C++ generally will not automatically provide a default constructor. You can define one explicitly if needed.

B. Parameterized constructor

A parameterized constructor accepts values when creating an object.

class Box {
private:
    int length;

public:
    Box(int value) : length(value) {}
};

Usage:

Box b(10);

C. Constructor overloading

A class can provide more than one constructor, as long as their parameter lists differ.


#include <iostream>
using namespace std;

class Box {
private:
    int length;
    int width;

public:
    Box() : length(1), width(1) {}

    Box(int side) : length(side), width(side) {}

    Box(int l, int w) : length(l), width(w) {}

    int area() const {
        return length * width;
    }
};

int main() {
    Box b1;
    Box b2(5);
    Box b3(4, 6);

    cout << b1.area() << endl;
    cout << b2.area() << endl;
    cout << b3.area() << endl;

    return 0;
}

Output

1
25
24

The compiler selects the constructor that matches the arguments supplied.

10. The this pointer

Inside a non-static member function, this is a pointer to the current object.

It is especially useful when parameter names and member names are the same.

Example 6 — Using this


#include <iostream>
#include <string>
using namespace std;

class Student {
private:
    string name;

public:
    void setName(string name) {
        this->name = name;
    }

    void display() const {
        cout << name << endl;
    }
};

int main() {
    Student s1;

    s1.setName("Aman");
    s1.display();

    return 0;
}

Output

Aman

In this->name = name;:

  • this->name refers to the current object's data member.

  • name refers to the function parameter.

The this pointer is available in non-static member functions. It is not available in static member functions because those functions do not operate on one particular object through an implicit this pointer.

11. Static data members and static member functions

A normal data member belongs to each object separately. A static data member belongs to the class and is shared by all its objects.

Example 7 — Count the number of objects


#include <iostream>
using namespace std;

class Student {
private:
    inline static int count = 0;

public:
    Student() {
        count++;
    }

    static int getCount() {
        return count;
    }
};

int main() {
    Student s1;
    Student s2;
    Student s3;

    cout << "Objects created: "
         << Student::getCount() << endl;

    return 0;
}

Output

Objects created: 3

Explanation:

  • inline static int count = 0; defines one shared counter. Inline static data members are supported in C++17 and later.

  • Each constructor call increments that shared counter.

  • Student::getCount() calls a static member function using the class name and scope resolution operator.

  • A static member function can directly access static members, but it cannot directly access non-static members without an object.

For older C++ standards, a static data member generally needs a separate definition outside the class, such as int Student::count = 0;, in one source file.

12. Destructors: cleaning up objects

A destructor is a special member function that runs when an object's lifetime ends. It is commonly used to release resources owned by the object, although modern C++ often relies on standard library types that manage resources automatically.

A destructor:

  • Has the class name preceded by ~.

  • Has no return type.

  • Takes no parameters.

  • Cannot be overloaded.

Example 8 — Destructor demonstration


#include <iostream>
using namespace std;

class Demo {
public:
    Demo() {
        cout << "Constructor called" << endl;
    }

    ~Demo() {
        cout << "Destructor called" << endl;
    }
};

int main() {
    Demo d;
    cout << "Inside main" << endl;

    return 0;
}

Output

Constructor called
Inside main
Destructor called

The local object d is destroyed when its scope ends. Its destructor runs automatically.

For a simple class that only stores an int or a string, you usually do not need to write a custom destructor. The members manage their own lifetimes. This is one reason modern C++ favors resource-managing library types over manually allocated memory.

13. Mini project — Bank account system

Now combine classes, private data, constructors, validation, and member functions into a practical program.

Features:

  • Store an account holder's name and balance.

  • Deposit money.

  • Withdraw money if sufficient funds are available.

  • Display the current balance.


#include <iostream>
#include <string>
using namespace std;

class BankAccount {
private:
    string holderName;
    double balance;

public:
    BankAccount(const string& name, double initialBalance)
        : holderName(name),
          balance(initialBalance >= 0 ? initialBalance : 0) {
    }

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            cout << "Deposit successful." << endl;
        } else {
            cout << "Invalid deposit amount." << endl;
        }
    }

    void withdraw(double amount) {
        if (amount <= 0) {
            cout << "Invalid withdrawal amount." << endl;
        } else if (amount > balance) {
            cout << "Insufficient balance." << endl;
        } else {
            balance -= amount;
            cout << "Withdrawal successful." << endl;
        }
    }

    void display() const {
        cout << "Account holder: " << holderName << endl;
        cout << "Balance: " << balance << endl;
    }
};

int main() {
    BankAccount account("Aman", 1000);

    account.display();

    account.deposit(500);
    account.withdraw(200);
    account.withdraw(2000);

    cout << "\nFinal account details:" << endl;
    account.display();

    return 0;
}

Expected output

Account holder: Aman
Balance: 1000
Deposit successful.
Withdrawal successful.
Insufficient balance.

Final account details:
Account holder: Aman
Balance: 1300

Project breakdown

  1. holderName and balance are private, so external code cannot modify them directly.

  2. The constructor initializes the account and prevents a negative starting balance.

  3. deposit() accepts only positive amounts.

  4. withdraw() rejects invalid amounts and withdrawals exceeding the available balance.

  5. display() const shows the current state without modifying it.

This is a learning example, not a production banking system. Real financial applications require stronger validation, precise monetary representations, persistence, authorization, and transaction safeguards.

Project extension challenges

Try adding these features:

  • Add a unique account number.

  • Add a transfer function that moves money between two accounts.

  • Keep a transaction history.

  • Add a function that returns the balance.

  • Add a menu-driven interface using a loop and switch.

14. Common mistakes

Mistake 1 — Forgetting the class semicolon

Incorrect:

class Student {
public:
    int marks;
}

Correct:

class Student {
public:
    int marks;
};

Mistake 2 — Accessing private members directly

class Account {
private:
    int balance = 100;
};

Account a;
// cout << a.balance;  // Error

Provide an appropriate public member function to expose the required operation or information.

Mistake 3 — Giving a constructor a return type

class Student {
public:
    // void Student() {} // This is not a constructor
    Student() {}
};

A constructor has the class name and no return type.

Mistake 4 — Confusing . and ->

Student s;
Student* ptr = &s;

s.marks = 80;
ptr->marks = 90;

Use . with an object and -> with a pointer to an object.

Mistake 5 — Assuming a parameter changes the original object

Passing Student s by value creates a copy. If a function needs to modify the caller's object, use a suitable reference parameter such as Student& s.

Mistake 6 — Overlooking initialization

Prefer constructors, default member initializers, or value initialization such as Student s{}; to ensure built-in data members have defined starting values.

15. Hands-on exercises

Try these exercises before checking the solutions.

Exercise 1 — Rectangle class

Create a Rectangle class with private length and width members. Add a constructor and a public function that returns the area.

Hint: Area = length × width.

Exercise 2 — Employee salary

Create an Employee class with private name and salary members. Initialize them using a constructor and provide a public function to display the employee details.

Hint: Use a constant reference for a string constructor parameter if you want to avoid copying the argument unnecessarily.

Exercise 3 — Counter class

Create a Counter class with a private integer and public functions increment(), reset(), and getValue().

Hint: Keep the counter data private and modify it through member functions.

Exercise 4 — Constructor overloading

Create a Box class with constructors that accept no arguments, one dimension, or two dimensions. Add an area() function.

Hint: Use three distinct parameter lists and initialize members in initializer lists.

Exercise 5 — Validated marks

Create a Student class with private marks. Add a setter that accepts marks only in the range 0–100 and a getter to return the current marks.

Hint: Reject invalid values without changing the previous valid marks.

Show exercise solutions

16. Interactive knowledge-check quiz

Test your understanding of classes and objects, access control, constructors, encapsulation, and member functions.

Given class Box { int length; };, what is the default access level of length?

1 of 8

Aprotected

Bpublic

Cstatic

Dprivate

Next

17. Module 11 completion checklist

Mark each topic as you master it.

Your progress

0 of 12

Explain the difference between a class and an object.

Create objects and access public members.

Explain public, private, and protected access.

Use encapsulation with getters, setters, and validation.

Declare and define member functions inside and outside a class.

Write default and parameterized constructors.

Use constructor overloading and initializer lists.

Explain the this pointer.

Use static data members and static member functions.

Explain when destructors run.

Complete the Bank Account System mini project.

Pass the interactive quiz and solve the exercises.