OOP Notes
OOP Notes
▪ An object should have a defined state when one of its data members changes.
Definition
A constructor is a special function in a class that is automatically called
when an object is created. Its primary purpose is to initialize data
members.
Properties of a Constructor
▪ Same name as the class.
▪ No return type (not even void).
▪ Automatically called when an object is created.
class shoplist {
public:
shoplist() {
count = 0;
}
};
▪ The constructor shoplist() is automatically called when an object is
created.
▪ It initializes count to 0.
Parameterized Constructor
A constructor can take arguments to initialize an object with specific
values.
class A {
public:
string c;
A(string e)
{
c = e;
}
};
▪ A(string e) is a constructor with a parameter.
▪ It initializes c with the value passed as e.
A obj("hello c++");
▪ The object obj is created, and its c member is initialized with "hello c++".
Stack Allocation
When an object is created without new, it is allocated on the stack.
A a1;
A a2;
Heap Allocation
When an object is created using new, it is allocated on the heap.
A *c = new A("hello");
Properties of a Destructor
Example Destructor
class A {
public:
~A() {
cout << "Destructor called";
}
};
[Link] Objects
int main() {
A a1;
A b1;
}
▪ The destructors for a1 and b1 are automatically called when main() ends.
[Link] Objects
int main() {
A *c = new A("hello");
delete c; // Destructor called here
}
class A {
public:
A() { cout << "Constructor called" << endl; }
};
int main() {
A obj; // Constructor is called automatically
}
▪ Output: Constructor called
2. When a Heap Object is Created Using new
▪ If a class has a constructor with parameters, it is called when an object is initialized with arguments.
class A {
public:
A(string msg) { cout << "Constructor called with: " << msg << endl; }
};
int main() {
A obj("Hello"); // Parameterized constructor is called
}
class A {
public:
A() { cout << "Constructor called" << endl; }
};
int main() {
A arr[3]; // Constructor is called 3 times
}
▪ Output:
Constructor called
Constructor called
Constructor called
5. When an Object is Passed by Value
class A {
public:
A() { cout << "Default Constructor called" << endl; }
A(const A& obj) { cout << "Copy Constructor called" << endl; }
};
int main() {
A a1; // Default constructor is called
func(a1); // Copy constructor is called
}
▪ Output:
A func() {
A temp;
return temp;
}
int main() {
A obj = func(); // Copy constructor is called
}
7. When an Object is Created Using Another Object
A obj1;
A obj2 = obj1; // Copy constructor is called
▪ Constructors initialize objects automatically when they are created.
▪ For arrays, the constructor is called once per object in the array.
When is a Constructor Not Called in C++?
If you declare a pointer to an object without using new, the constructor is not called.
class A {
public:
A() { cout << "Constructor called" << endl; }
};
int main() {
A* obj; // Constructor is NOT called (only a pointer is declared)
}
Why?
▪ obj is just a pointer to an object, but no actual object is created.
▪ The constructor is only called when an object is instantiated, not just when a pointer is declared.
Types of constructors in C++:
Default Constructor
▪ A constructor that takes no parameters and initializes the object with default values.
class Point {
public:
int x, y;
Point() { x = 0; y = 0; } // Default constructor
};
Parameterized Constructor
▪ A constructor that takes arguments to initialize the object with specific values.
class Point {
public:
int x, y;
Point(int a, int b) { x = a; y = b; } // Parameterized constructor
};
Copy Constructor
▪ A constructor that creates a new object by copying an existing object.
class Point {
public:
int x, y;
Point(int a, int b) { x = a; y = b; }
Point(const Point &p) { x = p.x; y = p.y; } // Copy constructor
};
Calling Default Constructor
▪ The default constructor is automatically called when an object is created without passing any arguments.
class Point {
public:
int x, y;
// Default Constructor
Point() {
x = 0;
y = 0;
cout << "Default Constructor Called" << endl;
}
};
int main() {
Point p1; // Calls Default Constructor
return 0;
}
Calling Parameterized Constructor
▪ The parameterized constructor is called when an object is created with arguments.
class Point {
public:
int x, y;
// Parameterized Constructor
Point(int a, int b) {
x = a;
y = b;
cout << "Parameterized Constructor Called" << endl;
}
};
int main() {
Point p2(10, 20); // Calls Parameterized Constructor
return 0;
}
Calling Copy Constructor
▪ The copy constructor is called when a new object is initialized from an existing object.
class Point {
public:
int x, y;
// Parameterized Constructor
Point(int a, int b) {
x = a;
y = b;
}
// Copy Constructor
Point(const Point &p) {
x = p.x;
y = p.y;
cout << "Copy Constructor Called" << endl;
}
};
int main() {
Point p3(5, 15); // Calls Parameterized Constructor
Point p4 = p3; // Calls Copy Constructor
return 0;
}
Destructors in C++
A destructor is a special member function of a class that is automatically invoked when an object
goes out of scope or is explicitly deleted.
Properties of Destructors:
int main() {
A obj; // Constructor called
return 0; // Destructor called when obj goes out of scope
}
Heap Allocation and Destructor
If an object is dynamically allocated using new, its destructor is not called automatically.
You must explicitly use delete.
class A {
public:
A() { Output:
cout << "Constructor A\n";
} Constructor A
Destructor A
~A() {
cout << "Destructor A\n";
} If delete p; is omitted, the destructor will
}; not be called, causing a memory leak.
int main() {
return 0;
}
Feature Constructor Destructor
A Constructor H
A Constructor Q1
A Constructor B
A Constructor C
A Constructor P
A Destructor Q1
A Destructor C
A Constructor E
A Destructor P
A Destructor E
A Destructor B
A Destructor
▪ Objects created with new must be deleted using delete.
▪ Local (stack) objects are automatically destroyed when they go out of scope.
▪ Parameterized Constructor is used for "Q1", "B", "C", "P", and "E".
▪ Dynamically allocated objects (q1 and p) must be explicitly deleted using delete.
▪ Local objects (c, b, e, q) are automatically destroyed when they go out of scope.
▪ Objects are destroyed in reverse order of their creation (Last In, First Out - LIFO).
Difference Between Structure and Class in C++
};
class MyClass {
};
Function Overloading
Function overloading allows multiple functions to have the same name but different parameter lists
(different number or types of parameters).
int main() {
cout << add(2, 3) << endl; // Calls int version
cout << add(2.5f, 3.2f) << endl; // Calls float version
cout << add('A', 'B') << endl; // Calls char version
return 0;
}
Constructor Overloading
Just like functions, constructors can also be overloaded with different parameter lists.
▪ If a constructor with parameters is defined but no default constructor exists, then objects cannot
void display() {
cout << "(" << x << ", " << y << ")" << endl;
}
};
If a default constructor is missing, then creating an object without arguments will cause a
compilation error.
Example:
If we remove Default Constructor in previous program
Provides multiple
implementations of a Provides multiple ways
Purpose
function with the same to initialize an object
name
Not applicable
Cannot be used to
Return Type (constructors have no
differentiate
return type)
Default constructor is
No function is automatically provided if
Default Handling
automatically created no constructor is
defined
Encapsulation in C++
▪ Encapsulation hides complexity: A user only interacts with the main features (e.g., calling,
messaging) without seeing the internal circuits.
▪ Two principles:
▪ Restrict direct access to sensitive data.
▪ Provide necessary functionalities while hiding the implementation.
Access Specifiers
Specifier Accessibility
struct Point {
int x; // Public by default
int y;
int main() {
// Constructor Point j; // Default constructor
Point(int a = 0, int b = 0) { j.x = -5; // Allowed (since struct members are public)
if (a >= 0 && b >= 0) { j.y = -10; // Allows negative values, leading to errors
x = a;
return 0;
y = b;
}
} else {
cout << "Invalid input!\n";
}
}
};
Problem:
▪ Since struct members are public by default, we can directly modify x and y, allowing incorrect values.
▪ Fixing the Issue with class (Encapsulation using Private Members)
class Point { // Setter Methods (with validation)
private: void setX(int a) {
int x; if (a >= 0) x = a;
int y; else cout << "Invalid X
value!\n";
public: }
// Constructor with validation
Point(int a = 0, int b = 0) { void setY(int b) {
if (a >= 0 && b >= 0) { if (b >= 0) y = b;
x = a; else cout << "Invalid Y
y = b; value!\n";
} else { }
cout << "Invalid input!\n"; };
x = 0;
int main() {
y = 0; Point j; // Default constructor
}
} // j.x = -5; // Error: x is private (Encapsulation working)
// Getter Methods [Link](-5); // Output: Invalid X value!
[Link](10); // Correct
int getX() { return x; }
int getY() { return y; }
cout << "X: " << [Link]();
cout<< ", Y: " << [Link]() << "\n";
return 0;
}
▪ Encapsulation prevents direct modification of data.
▪ class is private by default, ensuring safety.
▪ Getters and Setters allow controlled access to private data.
▪ Validation inside constructors prevents invalid object creation.
▪ If a constructor is private, you cannot create objects directly.
▪ Auxiliary functions (helper functions) assist the main program but are not exposed
externally.
▪ Structures vs. Classes:
▪ Structure (struct): Everything is public by default.
▪ Class (class): Everything is private by default.
▪ Encapsulation ensures data security by restricting direct access to class members.
▪ Private data members can only be accessed through public getters and setters.
▪ Constructors validate inputs, preventing invalid object states.
▪ Encapsulation supports abstraction, hiding implementation details.
#include <iostream> int main() {
#include <cmath> Point p1(3, 4);
using namespace std; Point p2(6, 8);
#include <iostream>
#include <cmath>
class Point {
private:
int x;
int y;
public:
// Constructor
Point(int a = 0, int b = 0);
// Getter functions
int getX() const;
int getY() const;
// Setter functions
void setX(const int a);
void setY(const int b);
// Display function
void display() const;
#endif // POINT_H
2. Implementation File ([Link])
#include "Point.h" // Getter function definitions
using namespace std; int Point::getX() const { return x; }
int Point::getY() const { return y; }
// Constructor definition
Point::Point(int a, int b) { // Setter function definitions
if (a >= 0 && b >= 0) { void Point::setX(const int a) {
x = a; if (a >= 0) x = a;
y = b; else cout << "Invalid X value!\n";
} else { }
cout << "Invalid input!\n";
x = 0; void Point::setY(const int b) {
y = 0; if (b >= 0) y = b;
} else cout << "Invalid Y value!\n";
} }
#include "Point.h"
int main() {
Point p1(3, 4);
Point p2(6, 8);
[Link]();
[Link]();
std::cout << "Distance between points: " << [Link](p2) << std::endl;
return 0;
}
Notes
Inline Functions in C++
▪ Advantages:
▪ Important Note: The compiler decides whether to inline a function; the inline keyword is a
suggestion.
class Point {
public:
inline void print() {
cout << "X: " << x << ", Y: " << y;
}
};
What are Inline Functions?
An inline function in C++ is a function for which the compiler replaces the function call
with the actual function code during compilation. This avoids the overhead of a function
call, improving performance for small, frequently used functions.
Why Use Inline Functions?
1. Function calls introduce overhead Pushing arguments onto the stack.
2. Jumping to the function definition and executing it.
3. Returning control back to the caller.
▪ due to:
▪ For small functions, this overhead can be more expensive than the function itself.
▪ Inlining eliminates this by inserting the function code directly at the call site.
int main() {
Math obj;
cout << "Square of 5: " << [Link](5) << endl;
return 0;
}
How it Works?
▪ Instead of calling square(5), the compiler replaces it with 5 * 5 directly in main().
▪ This reduces execution time by eliminating function call overhead.
Inline Functions Inside a Class
▪ Functions defined inside a class are implicitly inline (even without the inline keyword).
▪ Example:
class Point {
public:
int x, y;
void show() { // Implicitly inline
cout << "X: " << x << ", Y: " << y << endl;
}
};
▪ Large functions should not be inlined (increases code size, reducing efficiency).
▪ Inlining does not work with virtual functions, as their calls are resolved at runtime.
What is the (this Pointer)?
▪ The (this pointer) in C++ is an implicit pointer available inside non-static member
functions of a class. It points to the current object that invokes the function.
▪ It can be used to differentiate instance variables from parameters when they have the
same name.
▪ The (this pointer) is not available in static functions because they belong to the class,
not any specific object.
Understanding this Pointer
class Example {
private:
int x;
public:
void setX(int x) {
this->x = x; // Using 'this' to differentiate between instance variable and parameter
}
void display() {
cout << "Value of x: " << this->x << endl;
}
};
int main() {
Example obj;
[Link](10);
[Link]();
return 0;
Explanation
}
▪ setX(int x) has a local variable x that shadows the instance variable x.
▪ this->x refers to the instance variable, while x (without this) refers to the parameter.
▪ this ensures that we are assigning the parameter value to the instance variable.
Returning this Pointer
▪ The (this pointer) can be used to return the current object.
class Example {
private:
int x;
public:
Example& setX(int x) {
this->x = x;
return *this; // Returning the current object
}
void display() {
cout << "Value: " << x << endl;
}
};
int main() {
Example obj;
[Link](20).display(); // Chained function calls
return 0;
}
Explanation
▪ setX(int x) assigns a value and returns *this (the current object).
▪ This allows method chaining, where we call display() directly after setX(20).
(this Pointer) in Copy Constructor
▪ The (this pointer) is useful in copy constructors to avoid self-assignment issues.
class Example {
private:
int x;
public:
Example(int x) { this->x = x; }
void display() {
cout << "Value: " << x << endl;
}
};
int main() {
Example obj1(50);
Example obj2 = obj1; // Calls copy constructor
[Link]();
[Link]();
return 0; Explanation
} ▪ The copy constructor Example(Example &obj) assigns values using this->x = obj.x.
▪ This ensures the new object gets the same value as obj1.
(this Pointer)
▪ The (this pointer) points to the current instance of the class.
▪ It is automatically passed to all non-static member functions.
class Point {
int x, y;
public:
void setX(int x) {
this->x = x;
}
};
▪ The const keyword is used to define constant values, functions, and objects that cannot be modified
after initialization.
const Variable
▪ A const variable is a read-only variable whose value cannot be changed after initialization.
int main() {
const int x = 10; // Constant integer
cout << "x = " << x << endl;
return 0;
}
A const function in a class does not modify the object's state. It is mainly used in getter
functions.
public:
Example(int val) { x = val; }
A const object is an instance of a class that cannot modify its own data.
class Example {
private:
int x;
public:
Example(int val) { x = val; }
int main() {
const Example obj(10); // `const` object
static Variable
A static variable inside a class is shared among all instances of that class.
int main() {
Example obj1, obj2, obj3;
cout << "Total Objects: " << Example::getCount() << endl; // Output: 3
return 0;
}
static Function
A static function belongs to the class rather than any instance. It can only access static
members.
public:
static void showCount() { ▪ static functions do not require an object to be called.
cout << "Count: " << count << endl; ▪ They cannot access non-static members of the class.
}
};
int main() {
Example::showCount(); // Calling a static function
return 0;
}
Static Objects in C++
A static object is an object that persists throughout the program's execution. It is initialized only
once and retains its value between function calls.
▪ Lifetime: A static object is created only once and destroyed at the end of the program.
▪ Scope: If declared inside a function, it remains local to the function but retains its value
between calls.
▪ Initialization: It is initialized only once when encountered for the first time.
void test() {
static Example obj; // Static object (created once)
[Link]();
}
class Example {
public:
Example() {
cout << "Constructor called" << endl;
} •The static object obj inside test() is created only once.
~Example() { •Even though test() is called multiple times, the object is not re-created.
cout << "Destructor called" << endl; •The destructor is called only when the program exits.
}
void display() {
cout << "Hello from Example class!" << endl;
}
};
void test() {
static Example obj; // Static object (created once)
Constructor called
[Link](); Hello from Example class!
} Hello from Example class!
Destructor called (after program exits)
int main() {
test(); // First call: Object is created and used
test(); // Second call: Same object is reused (no new constructor call)
return 0;
}
Member Initialization List in C++
class ClassName {
int a;
const int b;
public:
// Constructor using Member Initialization List
▪ This approach enhances code readability and reduces the need for intermediate variables.
▪ It is commonly used in scenarios such as configuring objects, performing sequential operations, and
implementing fluent APIs.
▪ A technique where multiple methods are called in a single statement.
▪ Each method returns a reference to the calling object (*this), enabling subsequent method calls.
[Link]().append(" World").show();
[Link]();
[Link](" World");
[Link]();
Student& showCourses() {
cout << "Student: " << name << "\nCourses: " << ([Link]() ? "None" : courses) << endl;
return *this;
}
};
Chaining Usage:
Output:
[Link]("Math").addCourse("Physics").showCourses().addCourse("ComputerScience").showCourses(); Student: Ali
Courses: Math, Physics
Student: Ali
Returning *this from showCourses() doesn’t mean the result must be stored. It just allows Courses: Math, Physics, Computer Science
seamless chaining, making the code more concise
class Rectangle { int main() {
private: Rectangle r;
int width, height;
public: [Link](5).setHeight(10).showArea().setWidth(7).setHeight(3).showArea();
Rectangle() : width(0), height(0) {} return 0;
}
Rectangle& setWidth(int w) {
width = w;
return *this;
}
Rectangle& setHeight(int h) {
height = h;
return *this;
}
Rectangle& showArea() {
cout << "Area = " << (width * height) << endl;
return *this;
}
};
BankAccount& showBalance() {
cout << "Balance: $" << balance << endl;
return *this; Output:
} Balance: $150
}; Returning *this from showBalance() doesn’t mean the result Balance: $120
must be stored. It just allows seamless chaining, making the Insufficient funds!
code more concise Balance: $120
class StringFormatter { int main() {
private: StringFormatter sf("Hello");
string text; [Link]().show().append(" World!").show().toLowerCase().show();
public:
return 0;
StringFormatter(string str) : text(str) {}
}
StringFormatter& toUpperCase() {
for (char &c : text) c = toupper(c);
return *this;
}
StringFormatter& toLowerCase() {
for (char &c : text) c = tolower(c);
return *this;
}
StringFormatter& show() {
cout << "Formatted String: " << text << endl; Output:
return *this; Formatted String: HELLO
} Returning *this from show() doesn’t mean the result must be Formatted String: HELLO World!
}; stored. It just allows seamless chaining, making the code Formatted String: hello world!
more concise
class FileWriter {
private:
ofstream file;
public:
FileWriter(string filename) { int main() {
[Link](filename); FileWriter fw("[Link]");
} [Link]("Hello, World!").write("This is C++ chaining
example.").close();
FileWriter& write(string text) { return 0;
if (file.is_open()) { }
file << text << endl;
}
return *this;
}
FileWriter& close() {
if (file.is_open()) {
[Link]();
}
return *this;
}
};
class Rectangle {
private: int main() {
double length, width; Rectangle r;
public: [Link](10).setWidth(5).showArea();
Rectangle() : length(0), width(0) {} return 0;
}
Rectangle& setLength(double l) {
length = l;
return *this;
}
Rectangle& setWidth(double w) {
width = w;
return *this;
}
Rectangle& showArea() {
cout << "Area: " << length * width << endl;
return *this;
}
};
class Query {
private: int main() {
string query; Query q;
public: [Link]("age > 18").orderBy("name").execute();
Query() : query("SELECT * FROM students") {} return 0;
}
Query& where(string condition) {
query += " WHERE " + condition;
return *this;
}
Query& execute() {
cout << "Executing: " << query << endl;
return *this;
}
};