0% found this document useful (0 votes)
15 views72 pages

Unit 5

This document covers Unit 5 of a programming course focused on C++, specifically on polymorphism, overloading, and smart pointers. It explains key concepts such as compile-time and run-time polymorphism, function and operator overloading, virtual functions, and type conversion. Additionally, it discusses memory management techniques, including smart pointers and the importance of virtual destructors.

Uploaded by

Vansh Patel
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views72 pages

Unit 5

This document covers Unit 5 of a programming course focused on C++, specifically on polymorphism, overloading, and smart pointers. It explains key concepts such as compile-time and run-time polymorphism, function and operator overloading, virtual functions, and type conversion. Additionally, it discusses memory management techniques, including smart pointers and the importance of virtual destructors.

Uploaded by

Vansh Patel
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 72

PROGRAMMING WITH C++

UNIT 5
POLYMORPHISM, OVERLOADING, AND SMART POINTERS

Devang Patel Institute of Advance Technology and Research


Outlines
• Introduction to Polymorphism
• Function overloading
• overloading unary and binary operators and type conversion
• Relationships among Objects in an Inheritance Hierarchy
• Virtual Functions and Virtual Destructors
• Abstract Classes and Pure virtual Functions
• Runtime Polymorphism and Compile-Time Polymorphism
• Operator Overloading Fundamentals
• Dynamic Memory Management with new and delete
• Resource acquisition is initialization (RAII) and Smart Pointers
• Three-Way Comparison Operator(< = >)
• Converting Between Types
• Explicit Constructors and Conversion Operators
• Overloading the Function Call Operator ()
Introduction to Polymorphism in C++
• Polymorphism is one of the four key principles of Object-Oriented Programming
(OOP) in C++.
• It allows objects of different classes to be treated as objects of a common base class.
• Polymorphism enables flexibility and scalability in code by allowing a single
interface to control multiple types of behavior.

• Types of Polymorphism in C++


Polymorphism in C++ is categorized into two main types:
• Compile-time Polymorphism (Static Binding)
• Achieved using function overloading and operator overloading.
• Resolved at compile time.
• Run-time Polymorphism (Dynamic Binding)
• Achieved using virtual functions and base class pointers/references.
• Resolved at runtime.
Key Advantages of Polymorphism
• Code reusability: A single interface can be used for multiple
implementations.
• Extensibility: Adding new functionality is easier without modifying existing
code.
• Maintainability: Reduces code complexity by promoting modular design.
Compile-time Polymorphism
• Compile-time polymorphism, also known as static
polymorphism, occurs when the function call is resolved at
compile time rather than at runtime. This is achieved using:
• Function Overloading
• Operator Overloading
Function Overloading
• Function overloading allows multiple functions to have the same
name but different parameters (number, type, or both).
• The compiler determines which function to call based on the
arguments passed.
Example of Function Overloading
#include <iostream>
using namespace std; int main() {
Math obj;
class Math { cout << obj.add(5, 3) << endl; // Calls int version
public: cout << obj.add(2.5, 3.7) << endl; // Calls double version
// Overloaded function: same name, different parameters cout << obj.add(1, 2, 3) << endl; // Calls three-parameter version
int add(int a, int b) { return 0;
return a + b; }
}

double add(double a, double b) {


return a + b;
}

int add(int a, int b, int c) {


return a + b + c;
}
};

Output:
Operator Overloading
• Operator overloading allows customizing the behavior of
operators for user-defined types (like classes). This helps in
making objects interact naturally using operators like +, -, *, etc.,
similar to primitive types.

• Types of Operators:
• Unary Operators (+, -, ++, --)
• Binary Operators (+, -, *, /, %, ==, !=) Syntax:
• Special Operators ([], (), ->, =)
return_type operator<symbol>(arguments)
{
// Custom implementation
}
Overloading Binary Operators

#include <iostream> // Overloading the + operator


using namespace std; Complex operator+(Complex obj) {
return Complex(real + obj.real, imag + obj.imag);
class Complex { }
private:
int real, imag; void display() {
public: cout << real << " + " << imag << "i" << endl;
Complex(int r = 0, int i = 0) }
{ };
real=r;
imag=i; int main() {
} Complex c1(3, 2), c2(1, 7);
Complex c3 = c1 + c2; // Calls the overloaded operator+
c3.display();
return 0;
}
Output:
Overloading Unary Operators
#include <iostream>
using namespace std; void display() {
cout << "Value: " << value << endl;
class Count { }
private: };
int value;
public: int main() {
Count(int v = 0) : value(v) {} Count c(5);
++c; // Calls prefix ++
// Overloading prefix ++ c.display();
void operator++() {
++value; c++; // Calls postfix ++
} c.display();
// Overloading postfix ++ return 0;
void operator++(int) { }
value++;
}

Output:
Run-time Polymorphism
• Run-time polymorphism is achieved using function overriding
and virtual functions in C++.
• It allows a derived class to provide a specific implementation of a
function that is already defined in its base class.
• The function call is resolved at runtime using dynamic binding
(late binding).
Function Overriding (Virtual Functions)
• Function overriding allows a derived class to provide a specific
implementation of a function that is already defined in its base
class.
• The base class function is marked as virtual to enable dynamic.
• Virtual functions enable dynamic dispatch, meaning the function
that gets executed is determined at runtime.
Function Overriding Example
// C++ program to demonstrate compile time {
function overriding cout << "Derived Function" << endl;
}
#include <iostream> };
using namespace std;
int main()
class Parent { {
public: Child Child_Derived;
void GeeksforGeeks_Print() Child_Derived.GeeksforGeeks_Print();
{ return 0;
cout << "Base Function" << endl; }
}
};
Output:
class Child : public Parent {
public:
void GeeksforGeeks_Print()
Function Overriding (Virtual Functions) Example

#include <iostream>
using namespace std; int main() {
Animal* a; // Pointer to base class
class Animal { Dog d;
public: Cat c;
virtual void makeSound() { // Virtual function
cout << "Animal makes a sound." << endl; a = &d;
} a->makeSound(); // Calls Dog's makeSound() due to dynamic binding
};
a = &c;
class Dog : public Animal { a->makeSound(); // Calls Cat's makeSound() due to dynamic binding
public:
void makeSound() override { // Overriding function return 0;
cout << "Dog barks." << endl; }
}
};

class Cat : public Animal {


public:
void makeSound() override { // Overriding function
cout << "Cat meows." << endl;
} Output:
};
Pure Virtual Functions & Abstract Classes
• A pure virtual function is a function in a base class that must be
overridden by derived classes.
• It makes the base class an abstract class, meaning it cannot be
instantiated.

Syntax:
class Base {
public:
virtual void functionName() = 0; // Pure virtual function
};
Pure Virtual Functions & Abstract Classes Example

#include <iostream> cout << "Drawing a Rectangle." << endl;


using namespace std; }
};
class Shape {
public: int main() {
virtual void draw() = 0; // Pure virtual function (abstract class) Shape* s; // Pointer to base class
};
Circle c;
class Circle : public Shape { Rectangle r;
public:
void draw() override { // Overriding pure virtual function s = &c;
cout << "Drawing a Circle." << endl; s->draw(); // Calls Circle's draw()
}
}; s = &r;
s->draw(); // Calls Rectangle's draw()
class Rectangle : public Shape {
public: return 0;
void draw() override { // Overriding pure virtual function }

Output:
C++ Type Conversion
• Type conversion in C++ refers to converting one data type into
another. It can be categorized into implicit and explicit type
conversion.
Implicit Type Conversion (Type
Promotion)
• Also known as automatic type conversion, this happens when
the compiler automatically converts one data type into another
without explicit intervention by the programmer.

Rules for Implicit Conversion


• Conversion happens when assigning a value of a smaller data type to a
larger data type.
• There is no loss of data in integer promotions or widening conversions.
• Data loss may occur in narrowing conversions.
Example
#include <iostream>
using namespace std;

int main() {
int num = 10;
double dbl = num; // Implicit conversion from int to double Output:

cout << "num (int) = " << num << endl;


cout << "dbl (double) = " << dbl << endl; // Output: 10.0

return 0;
}
Explicit Type Conversion (Type Casting)
• Explicit conversion requires the programmer to specify the type conversion
manually using type casting. There are three ways to perform explicit type
conversion in C++:

• C-Style Cast
• C++ Static Cast (static_cast)
• Other C++ Casts
C++ provides other type casting methods for specific use cases:
•dynamic_cast (for polymorphic class conversions)
•const_cast (to add/remove const from variables)
•reinterpret_cast (for low-level pointer manipulation)
C-Style Cast ((type)variable)
#include <iostream>
using namespace std;

int main() {
double num = 9.99; Output:
int x = (int)num; // Explicit conversion using C-style cast

cout << "Original value (double): " << num << endl;
cout << "Converted value (int): " << x << endl; // Output: 9

return 0;
}
static_cast<> (Recommended in C++)
#include <iostream>
using namespace std;

int main() {
double num = 9.99;
int x = static_cast<int>(num); // Explicit conversion using static_cast

cout << "Original value (double): " << num << endl;
cout << "Converted value (int): " << x << endl; // Output: 9

return 0;
}

Output:
reinterpret_cast<> (For Pointer Conversion)
#include <iostream>
using namespace std;

int main() {
int num = 65;
char* ptr = reinterpret_cast<char*>(&num); // Reinterpreting int memory as char*

cout << "Reinterpreted character: " << *ptr << endl; // Output: 'A' (ASCII 65)

return 0;
}

Output:
const_cast<> (Removing const Qualifier)
#include <iostream>
using namespace std;

void modify(int* ptr) {


*ptr = 42;
}

int main() {
const int num = 10; Output:
int* ptr = const_cast<int*>(&num); // Removing const
modify(ptr);

cout << "Modified value: " << *ptr << endl; // Output: 42 (Undefined behavior warning!)

return 0;
}
dynamic_cast<> (For Polymorphic Classes)
#include <iostream>
using namespace std; if (derivedPtr) {
cout << "Successfully casted to Derived!" << endl;
class Base { } else {
public: cout << "Failed to cast!" << endl;
virtual void show() {} // Virtual function makes Base }
polymorphic
}; delete basePtr;
return 0;
class Derived : public Base {}; }

int main() {
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
// Safe downcasting

Output:
User-Defined Type Conversion
• C++ allows defining custom type conversions in classes by using:
• Conversion constructors
• Conversion operators
Example: Conversion Constructor
#include<iostream>
using namespace std;
class MyClass {
public:
int value;
MyClass(int v) { value = v; } // Conversion constructor
};
Output:
int main() {
MyClass obj = 100; // Implicit conversion from int to MyClass
cout << obj.value; // Output: 100
}
Example: Conversion Operator
#include<iostream>
using namespace std;
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}

operator int() { // Conversion operator


return value;
} Output:
};

int main() {
MyClass obj(200);
int x = obj; // Implicit conversion from MyClass to int
cout << x; // Output: 200
}
Relationships among Objects in an
Inheritance Hierarchy
• Inheritance in C++ establishes relationships among objects based on a
parent-child hierarchy. It allows a class (derived class) to inherit properties
and behaviors from another class (base class). This promotes code reuse,
abstraction, and polymorphism.
IS-A Relationship (Generalization &
Specialization)

• Represents "is a type of" relationship.


• A derived class is a specialized form of its base class.

Example:
• A Car is a Vehicle.
• A Dog is an Animal.
Example
#include <iostream>
using namespace std;

class Vehicle { // Base class


public:
void show() { cout << "I am a Vehicle" << endl; }
};

class Car : public Vehicle { // Derived class (Car is a Vehicle)


public: Output:
void display() { cout << "I am a Car" << endl; }
};

int main() {
Car myCar;
myCar.show(); // Inherited method
myCar.display(); // Car-specific method
return 0;
}
HAS-A Relationship
(Composition/Aggregation)
• Represents "has a" relationship.
• One class contains an instance (or pointer) of another class.

Example:
• A Car has a Engine.
• A Company has a Employee.
Example
#include <iostream>
using namespace std; int main() {
Car myCar;
class Engine { myCar.startCar();
public: return 0;
void start() { cout << "Engine Started" << endl; } }
};

class Car {
private:
Engine engine; // Car has an Engine
public:
void startCar() {
engine.start(); // Using Engine's functionality
cout << "Car is Moving" << endl;
}
};

Output:
Other Relationships
• POLYMORPHISM (Dynamic Behavior)
• A derived class can override the behavior of a base class.
• Achieved via function overriding and virtual functions.

• MULTIPLE INHERITANCE (Multiple Parent Classes)


• A class can inherit from more than one base class.
• Helps in combining functionalities from multiple sources.

• HIERARCHICAL INHERITANCE (Common Base Class)


• Multiple derived classes inherit from a single base class.

• HYBRID INHERITANCE (Combination of Types)


• Mixes multiple inheritance with hierarchical inheritance.
Virtual Functions
• A virtual function is a member function of a base class that can be
overridden in a derived class and supports dynamic binding (runtime
polymorphism).
• When a function is marked as virtual, C++ determines which function to
call at runtime instead of compile-time.

Key Features of Virtual Functions


• Declared in the base class using the virtual keyword.
• Allows function overriding in the derived class.
• Supports dynamic binding via base class pointers or references.
• Enables polymorphism (calling derived class methods through base
class pointers).
Example
#include <iostream>
using namespace std; int main() {
Base* basePtr; // Base class pointer
class Base { Derived obj;
public: basePtr = &obj;
virtual void show() { // Virtual function
cout << "Base class show()" << endl; basePtr->show(); // Calls Derived class show() due to
} dynamic binding
};
return 0;
class Derived : public Base { }
public:
void show() override { // Override the virtual function
cout << "Derived class show()" << endl;
}
};

Output:
Virtual Destructors
• A virtual destructor ensures that the destructor of a derived
class is called properly when deleting an object through a base
class pointer.

• Why Do We Need a Virtual Destructor?


• Without a virtual destructor, only the base class destructor runs, leading
to memory leaks when a derived class dynamically allocates memory.
Example
#include <iostream>
using namespace std;

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

class Derived : public Base {


public: Output:
~Derived() { cout << "Derived Destructor" << endl; }
};

int main() {
Base* ptr = new Derived();
delete ptr; // Calls both Base and Derived destructors

return 0;
}
Abstract Classes and Pure Virtual Functions
• In C++, abstract classes and pure virtual functions play a crucial
role in defining interfaces and enabling polymorphism.
• They allow us to create base classes that define a common
structure for derived classes while enforcing the implementation
of specific functions.
Pure Virtual Functions
• A pure virtual function is a function that must be overridden in a
derived class. It is declared in a base class with = 0.

Syntax: virtual ReturnType FunctionName() = 0;


Example
#include <iostream> int main() {
using namespace std; // Animal a; // ❌ ERROR: Cannot instantiate an abstract
class
class Animal { Dog d;
public: d.makeSound(); // Output: Woof! Woof!
virtual void makeSound() = 0; // Pure virtual function return 0;
}; }

class Dog : public Animal {


public:
void makeSound() override {
cout << "Woof! Woof!" << endl;
}
};

Output:
Abstract Classes
• An abstract class is a class that cannot be instantiated on its
own and is meant to be a base class for other classes.
• It contains at least one pure virtual function.

• Key Features of Abstract Classes


• Cannot create objects of an abstract class.
• Used to define a common interface for derived classes.
• Must have at least one pure virtual function.
• Derived classes must implement all pure virtual functions.
Example
#include <iostream>
using namespace std; int main() {
// Shape s; // ❌ ERROR: Cannot instantiate an abstract
class Shape { // Abstract class class
public: Circle c;
virtual void draw() = 0; // Pure virtual function c.draw(); // Output: Drawing a Circle
};
return 0;
class Circle : public Shape { }
public:
void draw() override {
// Implementing pure virtual function
cout << "Drawing a Circle" << endl;
}
};

Output:
Example Abstract Class with Concrete
Functions
#include <iostream> };
using namespace std;
int main() {
class Animal { Cat c;
public: c.breathe(); // Output: Breathing... (from base class)
virtual void makeSound() = 0; // Pure virtual function c.makeSound(); // Output: Meow! (overridden)
void breathe() { // Concrete function return 0;
cout << "Breathing..." << endl; }
}
};

class Cat : public Animal {


public:
void makeSound() override {
cout << "Meow!" << endl;
}

Output:
Dynamic Memory Management with new and delete

• In C++, dynamic memory management allows you to allocate and


deallocate memory at runtime using new and delete.
• This is useful when the size of an object is unknown at compile-
time or needs to be flexible.

Why Use Dynamic Memory?


• Memory is allocated at runtime (heap memory).
• Enables dynamic arrays and flexible object creation.
• Prevents stack overflow when dealing with large objects.
• Necessary when working with pointers and polymorphism.
Allocating Memory with new
• The new operator allocates memory on the heap and returns a
pointer to it.

Syntax:
DataType* pointer = new DataType; // Allocates memory for a single variable
DataType* arrayPointer = new DataType[size]; // Allocates memory for an array
Deallocating Memory with delete
• The delete operator frees dynamically allocated memory to
prevent memory leaks.

Syntax:

delete pointer; // Frees memory for a single variable


delete[] pointer; // Frees memory for an array
Example
#include <iostream>
using namespace std;

int main() {
int* ptr = new int; // Allocates memory for an integer Output:
*ptr = 42; // Assign value to allocated memory

cout << "Value: " << *ptr << endl; // Output: 42

delete ptr; // Free allocated memory


return 0;
}
Example of Allocating a Single Object
#include <iostream>
using namespace std;

class Car {
public:
void start() {
cout << "Car is starting..." << endl;
}
};
Output:
int main() {
Car* myCar = new Car; // Allocates memory for a Car object
myCar->start(); // Call member function

delete myCar; // Free allocated memory


return 0;
}
Example of Allocating Arrays of Objects
#include <iostream>
using namespace std;

class Car {
public:
Car() { cout << "Car Created" << endl; }
~Car() { cout << "Car Destroyed" << endl; } Output:
};

int main() {
Car* cars = new Car[3]; // Allocates an array of 3 Car objects

delete[] cars; // Properly delete the array of objects


return 0;
}
Resource Acquisition Is Initialization (RAII) and Smart Pointers

• Resource Acquisition Is Initialization (RAII) is a fundamental memory


management and resource management technique in C++.

• It ensures that resources such as memory, file handles, sockets, and locks
are properly released when an object goes out of scope.

• Smart pointers in C++ automate resource management by wrapping raw


pointers and ensuring proper cleanup.
What is RAII?
• RAII (Resource Acquisition Is Initialization) is a design pattern
where:
• A resource (e.g., heap memory, file, lock) is acquired in the constructor.
• The resource is released in the destructor automatically when the
object goes out of scope.
Example
#include <iostream> int main() {
using namespace std; { // Scope block
RAII obj(42);
class RAII { // Memory is allocated inside the RAII object
private: } // `obj` goes out of scope → Destructor is called → Memory is freed
int* data;
// Pointer to dynamically allocated memory return 0;
public: }
RAII(int value) {
data = new int(value);
// Resource acquisition (allocating memory)
cout << "Resource allocated: " << *data << endl;
}

~RAII() {
delete data; // Resource release (freeing memory)
cout << "Resource deallocated" << endl; Output:
}
};
How RAII Works in previous Example
• Constructor allocates memory (new int(value)).
• Destructor releases memory (delete data).
• When obj goes out of scope, the destructor is called automatically.
• No manual delete is needed, preventing memory leaks.
Smart Pointers
• C++ smart pointers (unique_ptr, shared_ptr, weak_ptr) in
<memory> automate RAII for dynamically allocated objects.
Smart Pointers
• unique_ptr: Exclusive Ownership
• A unique_ptr ensures only one pointer manages an object. When it goes out of scope, it automatically deletes the
resource.

• shared_ptr: Shared Ownership


• A shared_ptr allows multiple pointers to share ownership of a resource. It uses reference counting to track
ownership.

• weak_ptr: Non-Owning Reference


• A weak_ptr is used to avoid circular references in shared_ptr.
Example of unique_ptr
#include <iostream> unique_ptr<Car> car2 = move(car1);
#include <memory> // Required for smart pointers // ✅ Transfer ownership using move()
using namespace std; if (!car1) {
cout << "car1 is now empty\n";
class Car { }
public:
Car() { cout << "Car Created\n"; } return 0;
~Car() { cout << "Car Destroyed\n"; } } // `car2` goes out of scope, Car is destroyed
void drive() { cout << "Driving...\n"; }
};

int main() {
unique_ptr<Car> car1 = make_unique<Car>(); // Creates Car object
car1->drive();

// unique_ptr does not allow copy


// unique_ptr<Car> car2 = car1; ❌ ERROR

Output:
Example of shared_ptr
#include <iostream>
#include <memory> // Required for smart pointers car2->drive();
using namespace std;
return 0; // Car is destroyed when last shared_ptr goes out of scope
class Car { }
public:
Car() { cout << "Car Created\n"; }
~Car() { cout << "Car Destroyed\n"; }
void drive() { cout << "Driving...\n"; }
};

int main() {
shared_ptr<Car> car1 = make_shared<Car>(); // Car is created
shared_ptr<Car> car2 = car1; // Shared ownership

cout << "Reference count: " << car1.use_count() << endl; // Output: 2

Output:
Example of weak_ptr
#include <iostream> int main() {
#include <memory> shared_ptr<A> a = make_shared<A>();
using namespace std; shared_ptr<B> b = make_shared<B>();

class B; // Forward declaration a->b_ptr = b; // Weak reference (does not increase reference count)
b->a_ptr = a; // Shared reference (increases reference count)
class A {
public: return 0; // Both objects are destroyed correctly
weak_ptr<B> b_ptr; }
// Weak reference to avoid circular dependency
~A() { cout << "A Destroyed\n"; }
};

class B {
public:
shared_ptr<A> a_ptr;
~B() { cout << "B Destroyed\n"; }
};

Output:
Three-Way Comparison Operator(< = >)
• The three-way comparison operator (<=>), also called the
spaceship operator, was introduced in C++20. It simplifies
comparisons by automatically generating ==, <, >, <=, and >=
operators.

What is the <=> Operator?


• The <=> operator compares two values and returns:
•0 if both are equal
•A negative value if the left is less than the right
•A positive value if the left is greater than the right
• Automatically generates comparison operators (==, <, >, <=, >=) when used inside a class.
Example
#include <iostream>
#include <compare> // Required for `<=>`
using namespace std;

int main() {
int a = 5, b = 10;

// Using the three-way comparison operator


auto result = a <=> b;

if (result < 0)
cout << "a is less than b\n";
else if (result > 0)
cout << "a is greater than b\n";
else
cout << "a is equal to b\n";

return 0;
}
Explicit Constructors and Conversion
Operators
• C++ allows implicit and explicit conversions between types.
• However, sometimes implicit conversions can cause unexpected
behaviors.
• To control this, explicit constructors and explicit conversion
operators are used.
Explicit Constructor
• By default, a single-argument constructor can be used for implicit
conversion, but marking it as explicit prevents unintended
conversions.

• When to Use explicit?


• Use explicit for constructors with one argument to prevent accidental conversions.
• Use explicit for conversion operators when implicit conversion could be dangerous.
• If implicit conversion improves usability, do not use explicit.
Implicit Constructor (Without explicit)
#include <iostream>
using namespace std; int main() {
printNumber(10); // Implicit conversion: int → Number
class Number { return 0;
public: }
int value;

// Constructor WITHOUT explicit (Allows implicit conversion)


Number(int v) : value(v) {
cout << "Constructor called\n";
}
};

void printNumber(Number n) {
cout << "Number: " << n.value << endl;
}

Output:
Explicit Constructor (Prevents Implicit
Conversion)
#include <iostream>
using namespace std; int main() {
// printNumber(10); // ❌ ERROR: Implicit conversion is blocked
class Number {
public: printNumber(Number(10)); // ✅ OK: Explicit conversion using
int value; constructor
return 0;
// Constructor WITH explicit (Prevents implicit conversion) }
explicit Number(int v) : value(v) {
cout << "Constructor called\n";
}
};

void printNumber(Number n) {
cout << "Number: " << n.value << endl;
}

Output:
Conversion Operators
• Conversion operators allow converting a class object into
another type.
• Implicit conversion operators allow automatic conversion.
• Explicit conversion operators require manual conversion.
Implicit Conversion Operator
#include <iostream> Number num(42);
using namespace std; int x = num; // Implicitly converts Number → int

class Number { cout << "Converted int: " << x << endl; // Output: 42
public: return 0;
int value; }

Number(int v) : value(v) {}

// Implicit conversion operator


operator int() const {
return value;
}
};

int main() {

Output:
Explicit Conversion Operator
#include <iostream> int main() {
using namespace std; Number num(42);

class Number { // int x = num; // ❌ ERROR: Implicit conversion is blocked


public:
int value; int x = static_cast<int>(num); // ✅ Explicit conversion
cout << "Converted int: " << x << endl;
Number(int v) : value(v) {}
return 0;
// Explicit conversion operator }
explicit operator int() const {
return value;
}
};

Output:
Overloading the Function Call Operator ()
• The function call operator () can be overloaded in C++ to allow an
object to be used like a function.
• This is useful for functors (function objects), which are commonly
used in STL algorithms, callbacks, and functional programming.

class ClassName {
public:
ReturnType operator()(ParameterList) {
Syntax: // Function body
}
};
Example
#include <iostream> int main() {
Adder add5(5); // Create an object with value 5
class Adder {
private: std::cout << "add5(10): " << add5(10) << std::endl; // Calls
int value; operator(), output: 15
public:
Adder(int val) : value(val) {} return 0;
}
// Overloading operator()
int operator()(int num) {
return value + num;
}
};

Output:
Example
#include <iostream>

class Multiplier {
public:
int operator()(int a, int b) {
return a * b;
}
};

int main() {
Multiplier multiply;
std::cout << "multiply(4, 5): " << multiply(4, 5) << std::endl; // Output: 20
Output:
return 0;
}
Example
#include <iostream> Counter counter;

class Counter { std::cout << counter() << std::endl; // Output: 1


private: std::cout << counter() << std::endl; // Output: 2
int count; std::cout << counter() << std::endl; // Output: 3
public:
Counter() : count(0) {} return 0;
}
// Overloading operator() to act like a counter
int operator()() {
Output:
return ++count;
}
};

int main() {

You might also like