Skip to content

Level 1 — Core OOP

1. Class & Object

1.1 What is a Class?

A class is a user-defined data type that groups data (variables) and behavior (functions). It acts as a blueprint. A blueprint for a house is not itself a house; similarly, a class is not an actual object. It creates a new user-defined type, meaning Student s; is conceptually similar to int x; or double y;.

Example

class Student {
public:
    string name;
    int age;

    void study() {
        cout << name << " is studying\n";
    }
};

How it works

The Student class can conceptually be viewed as:

Student
├── name
├── age
└── study()


1.2 What is an Object?

An object is a concrete instance of a class. When you create objects, each object gets its own copy of non-static data members, while member functions are not normally duplicated for every object. The function code is associated with the class, while each object has its own non-static data.

Example

Student s1;
Student s2;

s1.name = "Rahul";
s2.name = "Aman";
Here, Student is the class, and s1, s2 are objects.

How it works

These are separate member variables belonging to different objects. Conceptually:

s1
├── name = Rahul
└── age

s2
├── name = Aman
└── age

1.3 What is Stored in an Object?

Concept

An object primarily contains its non-static data members.

Example

class A {
public:
    int x;
    int y;

    void fun() {
        cout << x;
    }
};

A obj;

How it works

The object contains only x and y:

obj
├── x
└── y

Important: A common misconception is that every object contains its own copy of all member functions. This is generally not true. The member function itself does not normally contribute to the object's size.


1.4 Object Size

Output Question: Size of an object

1. The code

class A {
    int x;
    int y;

public:
    void fun() {}
};
What is sizeof(A) assuming standard 4-byte integers?

2. The reasoning The object only holds its non-static data members. Functions do not contribute to the object size. Here, we have two int variables. Assuming no padding issues:

x → 4 bytes
y → 4 bytes

3. The output

sizeof(A) = 8

Placement Tip: Object size can be affected by padding and alignment. You should not always obtain the size by blindly adding member sizes. For example, a class with char c; int x; may occupy 8 bytes rather than 5 bytes due to padding.


1.5 Object Creation: Dynamic vs Stack

There are two primary ways to create an object in C++:

Stack/Local Object:

Student s;

Dynamic Allocation:

Student* s = new Student();
For a dynamically created object using new, you must manually manage its lifetime by calling delete:
delete s;
This distinction becomes critical later with pointers, destructors, and virtual destructors.


1.6 Static Members Overview

When a class has static members, they are shared among all instances, whereas normal data is separate for each object.

Example

class Student {
public:
    int age;
    static int count;
};

Student s1;
Student s2;

How it works

age is separate for each object, while count is shared.

s1 → own age
s2 → own age

Student → one shared count
(Static members are covered in full detail under Section 7).


1.7 Common Interview Question: Class vs Object

Feature Class Object
Definition A definition / user-defined type / blueprint. A concrete instance of a class.
Memory Does not allocate memory (until instantiated). Allocates memory for data members.
Example class Car { public: int speed; }; Car c1; Car c2;

Placement Takeaway (Class & Object): * Class = user-defined type / blueprint. * Object = instance of a class. * Each normal object has its own non-static data members. * Member functions are not normally duplicated for every object. * Static data members are shared. * Object size depends on members, padding/alignment, and other implementation details.


2. Access Modifiers

2.1 Overview

C++ has three major access specifiers that control who can access a member: * public * private * protected

Access specifiers affect all members that follow them until another access specifier changes the access level. For example:

class A {
    int x;       // private by default
public:
    int y;       // public
private:
    int z;       // private
protected:
    int w;       // protected
};


2.2 The Access Modifiers

public

Public members can be accessed from outside the class wherever the object/member is otherwise accessible.

class Student {
public:
    string name;
};

// Outside code
Student s;
s.name = "Rahul"; // ✅ Valid

private

Private members can be accessed only from within the class itself.

class Student {
private:
    int marks;
public:
    void setMarks(int x) {
        marks = x; // ✅ Valid: Class accessing its own private member
    }
};

// Outside code
Student s;
// s.marks = 90; // ❌ Compilation error

protected

Protected members can be accessed inside the class itself and inside derived classes, but not normally through an object from unrelated outside code.

class Parent {
protected:
    int x;
};

class Child : public Parent {
public:
    void fun() {
        x = 10; // ✅ Valid: Derived class accessing protected member
    }
};

// Outside code
Child obj;
// obj.x = 10; // ❌ Compilation error


2.3 Access Visibility Table

Access Same Class Derived Class Outside
private ✅ ❌ ❌
protected ✅ ✅ ❌
public ✅ ✅ ✅

2.4 Important Distinction: Access Specifier vs Inheritance Mode

Important: There are two separate concepts involving these keywords: 1. The access level originally assigned to a member (private, protected, public). 2. The inheritance access mode used when deriving (public inheritance, protected inheritance, private inheritance).

For example:

class A {
protected:
    int x;
};

class B : private A {
};
Here x was protected in A, but through private inheritance, its accessibility through B becomes private. (Covered fully under inheritance).


2.5 class vs struct

Concept

In C++, both class and struct can have constructors, destructors, functions, inheritance, private/protected/public members, static members, etc.

Common Interview Trap: Thinking "struct is only for data and class is only for functions" is completely incorrect in C++.

The only difference is in their default access and inheritance modes.

Comparison

Feature class struct
Default member access private public
Default inheritance private public

Example

For a class:

class A {
    int x; // private by default
};

For a struct:

struct A {
    int x; // public by default
};

Placement Takeaway (Access Modifiers): * private → same class only. * protected → same class + derived classes. * public → accessible from outside as well. * class → private by default (both members & inheritance). * struct → public by default (both members & inheritance).


3. Encapsulation

3.1 What is Encapsulation?

Concept

Encapsulation is one of the four pillars of OOP. The basic idea is:

Keep an object's data and the operations on that data together, while controlling how the outside world can access that data.

Example

class BankAccount {
private:
    double balance;

public:
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    double getBalance() {
        return balance;
    }
};

How it works

Outside code cannot directly alter the balance (e.g., account.balance = -5000;) because it is private. Instead, it must go through controlled methods like account.deposit(1000);. The class controls its own state and can enforce rules.

Without encapsulation, anyone could set a salary to a negative value. With encapsulation, setters can validate inputs.

Encapsulation provides controlled access to an object's data/state.


3.2 Getters and Setters

Setter and getter methods provide a common mechanism for controlled access:

class Student {
private:
    int marks;

public:
    void setMarks(int x) { marks = x; }
    int getMarks() { return marks; }
};

Common Interview Trap: Encapsulation does NOT simply mean "using getters and setters." They are just one mechanism. A method like withdraw(int amount) that updates internal state is also part of an encapsulated design.


3.3 Encapsulation vs Data Hiding

They are related but should not be treated as exactly identical.

Concept Focus Mechanism
Encapsulation Bundling data + functions inside a class and controlling access. Class structures, methods.
Data Hiding Preventing direct access to implementation details. Using private and protected.

Data hiding is one mechanism used to achieve encapsulation.


3.4 Encapsulation vs Abstraction

Concept Focus
Encapsulation How data and behavior are bundled and access is controlled. Keeping internal state inaccessible.
Abstraction What should be exposed while hiding unnecessary implementation details. (e.g., car.start() hides internal engine ops).

Placement Takeaway (Encapsulation): * Data + behavior bundled in a class, with controlled access. * Do not equate encapsulation merely with getters/setters. * Encapsulation enforces rules on data manipulation.


4. Constructors

4.1 What is a Constructor?

Concept

A constructor is a special member function that is automatically called when an object is created.

Example

class Student {
public:
    Student() {
        cout << "Constructor called\n";
    }
};

Student s; // Automatically calls constructor
Output:
Constructor called

Important Rules (Constructor Properties)

  • Has the same name as the class.
  • Has no return type.
  • Automatically called during object construction.
  • Can be overloaded.
  • Can have parameters and default arguments.
  • Cannot be static.
  • Cannot be virtual.

4.2 Types of Constructors

Default Constructor

A constructor that can be called with no arguments.

class A {
public:
    A() { cout << "Hello"; }
};
A obj; // Works

Parameterized Constructor

A constructor that takes arguments.

class A {
public:
    A(int x) { cout << x; }
};
A obj(10); // Calls parameterized constructor

Constructor Overloading

A class can have multiple constructors as long as their parameter lists differ. This is an example of compile-time polymorphism.

class A {
public:
    A() {}
    A(int x) {}
    A(int x, int y) {}
};


4.3 Missing Default Constructor Trap

Output Question: Default Constructor Availability

1. The code

class A {
public:
    A(int x) {}
};

int main() {
    A obj;
}

2. The reasoning Since the user provided a parameterized constructor, the compiler does not auto-generate a default, no-argument constructor. Therefore, A obj; has no constructor to call.

3. The output

Compilation Error
To fix this, you must explicitly define A() {} or provide arguments like A obj(10);.


4.4 Member Initializer List

Concept

Instead of assigning inside the constructor body, we commonly initialize members using a list.

class Student {
    int age;
public:
    Student(int x) : age(x) { // Member initializer list
    }
};

How it works

Initialization happens before the constructor body executes. Conceptually: 1. Members are initialized (via list). 2. Constructor body executes.

When Initializer Lists are Mandatory

You must use initializer lists for: 1. const members: Must be initialized upon creation.

class A {
    const int x;
public:
    A(int value) : x(value) {} // Mandatory
};
2. Reference members: Must be bound to a variable upon creation.
class A {
    int& ref;
public:
    A(int& x) : ref(x) {} // Mandatory
};
3. Member objects requiring initialization: (Like an object member without a default constructor).
class Engine {
public:
    Engine() { cout << "Engine\n"; }
};

class Car {
    Engine e;
public:
    Car() { cout << "Car\n"; } // Output: Engine \n Car
};
The member Engine is constructed before Car's constructor body executes.


4.5 Member Initialization Order (Very Important)

Important: Data members are initialized in their declaration order, NOT the order in which they appear in the initializer list.

Output Question: Initialization Order

1. The code

class A {
    int x;
    int y;

public:
    A() : y(20), x(10) {
        cout << x << " " << y;
    }
};

2. The reasoning The declaration order is:

int x;
int y;
So x is initialized first, and y is initialized second, regardless of : y(20), x(10).

3. The output

10 20

Common Interview Trap: This is a classic C++ interview question. Always look at the variable declaration order to understand initialization sequences, as out-of-order lists can lead to bugs if members depend on one another.

Placement Takeaway (Constructors): * Same name, no return type, auto-invoked, can be overloaded, not virtual, not static. * Mandatory initializer list: const, references, non-default member objects. * Initialization happens in declaration order.


5. Destructors

5.1 What is a Destructor?

Concept

A destructor is a special member function responsible for cleanup when an object is destroyed or goes out of scope.

Example

class A {
public:
    ~A() {
        cout << "Destructor called\n";
    }
};

int main() {
    A obj;
} // Destructor is called automatically when obj goes out of scope
Conceptually:
Object created → Constructor → Object exists → Scope ends → Destructor

Important Rules (Destructor Properties)

  • Has the same name as the class preceded by ~ (e.g., ~ClassName()).
  • Has no return type and takes no parameters.
  • Cannot be overloaded (only one destructor exists per class).
  • Automatically called when an object is destroyed.
  • Can be virtual (Extremely important for polymorphism/inheritance).

5.2 When is a Destructor Called?

Scope Expiration

int main() {
    {
        A obj;
        cout << "Inside\n";
    }
    cout << "Outside\n";
}
Destruction happens right at the closing brace of the inner scope. The output order is Inside, then Destructor, then Outside.

delete Keyword

For dynamic objects:

A* p = new A(); // Calls Constructor
delete p;       // Calls Destructor, then releases memory


5.3 Multiple Objects: Destruction Order

Important: Destruction happens in the reverse order of construction (LIFO — Last In, First Out).

int main() {
    A a;
    A b;
    A c;
}
* Construction order: a → b → c * Destruction order: c → b → a


5.4 Destructors and Inheritance

Output Question: Base/Derived Destruction

1. The code

class Base {
public:
    ~Base() { cout << "Base "; }
};

class Derived : public Base {
public:
    ~Derived() { cout << "Derived "; }
};

int main() {
    Derived obj;
}

2. The reasoning Construction builds from the foundation up: Base → Derived. Destruction tears down from the top down: Derived → Base.

3. The output

Derived Base

Placement Takeaway (Destructors): * Syntax: ~ClassName(). No parameters, no return type, no overloading. * Destruction for multiple objects is LIFO (reverse of construction). * Inheritance: Construction (Base → Derived), Destruction (Derived → Base). * Virtual destructors are critical when deleting derived objects through base pointers.


6. this Pointer

6.1 What is this?

Concept

Inside a non-static member function, this is a pointer holding the memory address of the current object that invoked the function.

Example

class Student {
    int age;
public:
    void setAge(int age) {
        this->age = age;
    }
};

How it works

In the example above, there is a name collision between the parameter age and the member variable age. this->age = age; means current object's age = parameter age. Since this is a pointer, this->age is shorthand for (*this).age.

Without a collision, age = x; is perfectly valid.


6.2 this Changes With the Object

When the same method is called on different objects, this holds different addresses.

Student s1;
Student s2;

s1.setAge(20); // this = &s1
s2.setAge(22); // this = &s2
This is how a single shared copy of member function code operates on the correct object's data.


6.3 Returning *this for Method Chaining

A common C++ pattern is to return *this by reference.

class A {
    int x;
public:
    A& setX(int x) {
        this->x = x;
        return *this;
    }
};

A obj;
obj.setX(10).setX(20); // Method chaining
Because return *this; returns the current object itself by reference, subsequent methods can be called on it sequentially.


6.4 this in Static Functions

Important: this cannot be used in a static member function.

class A {
    int x;
public:
    static void fun() {
        // this->x = 10;  // ❌ Compilation Error
    }
};
Reasoning: A static member function belongs to the class itself, not to any particular object. Since there is no "current object," there is no this pointer available.

Placement Takeaway (this Pointer): * this is a pointer to the current object. * this->x specifies the object's member x. * Used to resolve naming collisions and for method chaining (return *this). * Not available in static functions.


7. Static Members

7.1 Static Data Members

Concept

A static data member belongs to the class, rather than to each individual object. There is only one shared copy of the variable for all objects of that class.

Example

class Student {
public:
    static int count; // Declaration
};

int Student::count = 0; // Definition outside the class

How it works

If you create multiple objects, they all share the exact same count:

Student s1;
Student s2;

Student::count++;
Student::count++;

cout << Student::count; // Output: 2
Conceptually:
s1 → own age
s2 → own age
Student → one shared count (used by all objects)

Important Rules

  • Traditionally, declaring the static member inside the class is not enough; it must be defined outside the class.
  • It can technically be accessed via objects (s1.count = 10;), but the preferred syntax is ClassName::member (Student::count) to clarify that it belongs to the class.

7.2 Output Question: Static Data Modification

1. The code

class A {
public:
    static int x;
};

int A::x = 5;

int main() {
    A a;
    A b;
    a.x++;
    b.x++;
    cout << A::x;
}

2. The reasoning Because x is static, a.x and b.x point to the exact same shared variable. 5 increments to 6 (via a), then 6 increments to 7 (via b).

3. The output

7


7.3 Static Member Functions

A class can also have static member functions that belong to the class and can be called without creating an object.

class A {
public:
    static void fun() { cout << "Hello"; }
};

A::fun(); // Call without object

Static Function Restrictions

A static member function: 1. Has no this pointer. 2. Cannot directly access non-static data members or non-static member functions.

class A {
    int x;           // non-static
    static int y;    // static
public:
    static void fun() {
        // cout << x; // ❌ Error: no object to read x from
        cout << y;    // ✅ Valid: y is shared
    }
};

Placement Takeaway (Static Members): * Static Data: One shared copy for the entire class. * Static Functions: Belong to the class, lack a this pointer, and cannot access non-static members directly.


8. const Objects & const Member Functions

8.1 const Objects

Concept

When an object is declared const, its non-static state cannot normally be modified.

class Student {
public:
    int age;
    void setAge(int x) { age = x; }
};

const Student s;
// s.age = 20;    // ❌ Error
// s.setAge(20);  // ❌ Error


8.2 const Member Functions

Concept

By placing the const keyword after the parameter list, you promise that the member function will not modify the object's state through this.

Example

class Student {
public:
    int age;
    int getAge() const {
        return age; // ✅ Valid read
    }
};

const Student s;
cout << s.getAge(); // ✅ Valid

How it works

Inside a const member function, the this object is conceptually treated as const.

int getAge() const {
    age = 20; // ❌ Error: modifying state in a const function
}

Why Const Member Functions?

They allow read-only operations on const objects. A const object can only call const member functions.


8.3 Function Overloading on Constness

A class can have both a const and non-const version of the same function. The compiler selects the appropriate version based on the object's constness.

class A {
public:
    void fun() { cout << "non-const\n"; }
    void fun() const { cout << "const\n"; }
};

A a;
const A b;
a.fun(); // Outputs: non-const
b.fun(); // Outputs: const


8.4 mutable Exception

Normally, a const function cannot modify non-static data members. The exception is if a member is declared mutable.

class A {
    mutable int x;
public:
    void fun() const {
        x = 10; // ✅ Valid due to mutable keyword
    }
};
For placement preparation, remember the rule: const functions cannot alter object state, but mutable overrides this restriction.


8.5 Common Interview Trap: Calling Non-Const from Const

Output Question: Const mismatch

1. The code

class A {
public:
    void fun() { cout << "Hello"; }
};

int main() {
    const A obj;
    obj.fun();
}

2. The reasoning obj is a const object, which means it guarantees its state will not change. However, fun() is a non-const member function, which doesn't provide the compiler any guarantee that it won't modify the object. Therefore, the compiler prevents the call.

3. The output

Compilation Error
To fix this, define fun() as: void fun() const { cout << "Hello"; }.

Placement Takeaway (const Members): * A const object cannot normally be modified. * A const object can only call const member functions. * A const member function cannot modify non-static data (unless declared mutable).


Final Self-Check

(Before considering Level 1 mastered, ensure you can answer these without hesitation.)

  1. What is the difference between a class and an object?
  2. Does every object have its own copy of a member function?
  3. What determines object size?
  4. What are private, protected, and public?
  5. What are the default access levels of class and struct?
  6. What is encapsulation?
  7. Is encapsulation the same thing as getters/setters?
  8. What is a constructor?
  9. What is constructor overloading?
  10. What is a member initializer list?
  11. Why must const and reference members be initialized?
  12. What determines member initialization order?
  13. What is a destructor?
  14. In what order are multiple objects destroyed?
  15. What does this point to?
  16. Why can't a static function use this?
  17. What is a static data member?
  18. Why is a static member shared?
  19. Can a static member function directly access a non-static member?
  20. What is a const member function?
  21. Can a const object call a non-const member function?
  22. Can a const member function read a normal data member?
  23. What is the purpose of mutable?
  24. What happens when a derived object is constructed/destroyed?