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";
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() {}
};
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();
new, you must manually manage its lifetime by calling delete:
delete s;
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
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 (publicinheritance,protectedinheritance,privateinheritance).
For example:
class A {
protected:
int x;
};
class B : private A {
};
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 "
structis only for data andclassis 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→privateby default (both members & inheritance). *struct→publicby 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
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
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
};
class A {
int& ref;
public:
A(int& x) : ref(x) {} // Mandatory
};
class Engine {
public:
Engine() { cout << "Engine\n"; }
};
class Car {
Engine e;
public:
Car() { cout << "Car\n"; } // Output: Engine \n Car
};
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;
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
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";
}
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;
}
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
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
return *this; returns the current object itself by reference, subsequent methods can be called on it sequentially.
6.4 this in Static Functions¶
Important:
thiscannot be used in astaticmember function.
class A {
int x;
public:
static void fun() {
// this->x = 10; // ❌ Compilation Error
}
};
this pointer available.
Placement Takeaway (
thisPointer): *thisis a pointer to the current object. *this->xspecifies the object's memberx. * Used to resolve naming collisions and for method chaining (return *this). * Not available instaticfunctions.
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
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 isClassName::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
thispointer, 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
}
};
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
fun() as: void fun() const { cout << "Hello"; }.
Placement Takeaway (
constMembers): * Aconstobject cannot normally be modified. * Aconstobject can only callconstmember functions. * Aconstmember function cannot modify non-static data (unless declaredmutable).
Final Self-Check¶
(Before considering Level 1 mastered, ensure you can answer these without hesitation.)
- What is the difference between a class and an object?
- Does every object have its own copy of a member function?
- What determines object size?
- What are
private,protected, andpublic? - What are the default access levels of
classandstruct? - What is encapsulation?
- Is encapsulation the same thing as getters/setters?
- What is a constructor?
- What is constructor overloading?
- What is a member initializer list?
- Why must
constand reference members be initialized? - What determines member initialization order?
- What is a destructor?
- In what order are multiple objects destroyed?
- What does
thispoint to? - Why can't a static function use
this? - What is a static data member?
- Why is a static member shared?
- Can a static member function directly access a non-static member?
- What is a const member function?
- Can a const object call a non-const member function?
- Can a const member function read a normal data member?
- What is the purpose of
mutable? - What happens when a derived object is constructed/destroyed?