UNIT 5: Object-Oriented Programming & Methodology
1. Fundamentals of OOP and C++ Overview
Core Features of OOP
-
Abstraction: Hiding implementation details, showing only essential features. Achieved via abstract classes/interfaces.
-
Encapsulation: Bundling data (attributes) and methods (functions) that operate on that data into a single unit (class). Restricts direct access using access specifiers.
-
Inheritance: Creating new classes (derived) from existing classes (base), promoting code reuse and establishing relationships.
-
Polymorphism: "Many forms." Ability of an object to take different forms. Implemented via function overloading (compile-time) and virtual functions (runtime).
Advantages of OOP Paradigm
-
Modularity: Code is organized into classes, making it easier to develop, debug, and maintain.
-
Reusability: Inheritance allows reuse of existing code, reducing redundancy.
-
Data Hiding: Protects data from accidental modification, enhancing security.
-
Easy Maintenance & Scalability: Changes in one part of the system often have minimal impact on others.
-
Real-world Modeling: Closer mapping to real-world entities and relationships.
C++ vs. C Language
| Feature | C Language | C++ Language |
|---|---|---|
| Paradigm | Procedural | Multi-paradigm (Procedural, OOP, Generic) |
| Data & Functions | Separate; functions operate on global/external data | Bundled together in classes (encapsulation) |
| Security | Less secure; data is globally accessible | More secure via access specifiers (private, protected) |
| Inheritance | Not supported | Supported (single, multiple, multilevel, etc.) |
| Polymorphism | Not supported | Supported (function/operator overloading, virtual functions) |
| Memory Management | malloc(), calloc(), free() |
new, delete operators (type-safe, call constructors/destructors) |
| Input/Output | scanf(), printf() (functions) |
cin, cout (streams, type-safe, extensible) |
| Error Handling | Error codes, errno |
Exception Handling (try, catch, throw) |
| Other | No function overloading, no namespaces, no templates | Supports function overloading, namespaces, templates |
Variable Declaration Scope in C++
-
Significance: In C, all variables must be declared at the beginning of a block (before any executable statement). C++ allows declaration anywhere within a scope (block).
-
Benefit: Improves code readability and locality. Variables can be declared and initialized close to where they are first used, reducing their lifetime and potential for errors. Enables declaring variables inside
forloops (e.g.,for(int i=0; ...)).
2. Classes, Objects, and Member Management
Class vs. Structure in C++
| Feature | class |
struct |
|---|---|---|
| Default Access | private |
public |
| Primary Use | For objects with data hiding, complex behavior | For passive data structures (Plain Old Data - POD) |
| Inheritance | Supported | Supported (same as class) |
| Member Functions | Can have constructors, destructors, methods | Can have constructors, destructors, methods (C++11 onwards) |
| Example | class Account { private: double balance; public: void deposit(); }; |
struct Point { int x, y; }; |
Access Specifiers
-
public: Members accessible from anywhere (outside class, derived classes, other functions). -
private: Members accessible only within the same class (default forclass). Not accessible by derived classes or outside code. -
protected: Members accessible within the same class and by derived classes. Not accessible from outside the inheritance hierarchy.
Access Specifiers in Inheritance
| Inheritance Type | Base public → Derived |
Base protected → Derived |
Base private → Derived |
|---|---|---|---|
public |
public → public<br>protected → protected |
public → protected<br>protected → protected |
public → private<br>protected → private |
protected |
public → protected<br>protected → protected |
public → protected<br>protected → protected |
public → private<br>protected → private |
private |
public → private<br>protected → private |
public → private<br>protected → private |
public → inaccessible<br>protected → inaccessible |
Data Encapsulation vs. Information Hiding
-
Encapsulation: The mechanism of bundling data and methods together. It's about packaging.
-
Information Hiding: The principle of hiding internal implementation details and exposing only a stable interface. Achieved using
private/protectedmembers. -
Relationship: Encapsulation enables information hiding. A class can be encapsulated without fully hiding information (if all members are
public).
Object Creation and Usage
-
Defining and Instantiating:
class Box { public: double length; }; Box myBox; // Object (instance) of class Box myBox.length = 5.0; -
Array of Objects:
Box arr[3]; // Array of 3 Box objects arr[0].length = 1.0; -
Pointers to Objects:
Box* ptr = new Box(); // Dynamic allocation ptr->length = 2.0; delete ptr; -
Array of Objects via Pointer:
Box* arrPtr = new Box[3]; // Array of 3 objects on heap arrPtr[0].length = 1.0; delete[] arrPtr; // Must use delete[]
Constructors and Destructors
-
Purpose: Constructor initializes object state. Destructor cleans up resources.
-
Types:
-
Default Constructor: No parameters. Called when object is created without arguments.
-
Parameterized Constructor: Takes arguments for initialization.
-
Copy Constructor:
ClassName(const ClassName& obj);Initializes a new object as a copy of an existing one.
-
-
Deep vs. Shallow Copy:
-
Shallow Copy: Default copy constructor copies member values bitwise. If class has pointer members, both objects point to same memory → dangling pointer/data corruption on destruction.
-
Deep Copy: User-defined copy constructor allocates new memory for pointer members and copies the data. Ensures independent objects.
class MyClass { int* data; public: // Deep copy constructor MyClass(const MyClass& src) { data = new int(*src.data); // Allocate and copy value } }; -
-
Constructor Overloading and Chaining:
-
A class can have multiple constructors with different signatures.
-
Chaining: One constructor calls another using member initializer list or within body (C++11: delegating constructors).
class Demo { int a, b; public: Demo() : Demo(0, 0) {} // Delegating to parameterized constructor (C++11) Demo(int x, int y) : a(x), b(y) {} }; -
-
Order of Constructor Calls (Inheritance): Base class constructor always executes first, then derived class constructor. Order follows inheritance hierarchy (top-down).
Member Functions
-
Inline Functions:
- Syntax: Defined inside class definition or using
inlinekeyword.
class Calc { public: inline int add(int x, int y) { return x+y; } // Inline };-
Purpose: Suggest compiler to insert function body at call site, avoiding function call overhead for small, frequently used functions.
-
vs. Macros:
| Feature | Inline Function | Macro (
#define) | | :--- | :--- | :--- | | Type Safety | Yes (type-checked by compiler) | No (textual substitution) | | Debugging | Easier (has a name, scope) | Difficult (no symbol) | | Side Effects | Safe (arguments evaluated once) | Unsafe (arguments may evaluate multiple times) | | Scope | Respects scope & access specifiers | Global textual replacement |
- Syntax: Defined inside class definition or using
-
Static Member Functions:
-
Belong to the class, not individual objects.
-
Can access only static data members and other static member functions.
-
Called using class name:
ClassName::staticFunc();
class Counter { static int count; public: static void inc() { count++; } }; int Counter::count = 0; Counter::inc(); // Call without object -
-
Friend Functions and Friend Classes:
- Friend Function: Non-member function granted access to
private/protectedmembers of a class.
class Box { double width; public: friend void setWidth(Box& b, double w); // Declaration }; void setWidth(Box& b, double w) { b.width = w; } // Definition- Friend Class: Entire class is granted access to another class's private members.
class FriendClass; // Forward declaration class MyClass { friend class FriendClass; // Friend class int secret; };- Use: Often used for operator overloading where left operand is not a class object (e.g.,
ostream& operator<<).
- Friend Function: Non-member function granted access to
Accessing Members
| Context | Access Mechanism | Example |
|---|---|---|
Inside main() |
Use object name + dot operator (.) or pointer + arrow (->) |
obj.memberFunc();<br>ptr->memberVar = 10; |
| Inside member function of same class | Direct access by name (implicit this pointer) |
memberVar = 5;<br>memberFunc(); |
| Inside member function of another class | Not directly accessible unless:<br>1. Members are public.<br>2. The other class is a friend.<br>3. Access via public interface (getter/setter methods) of the object. |
otherObj.publicMember;<br>otherObj.getPrivate(); |
3. Functions and Operator Overloading
Function Overloading
-
Definition: Multiple functions with same name but different parameter lists (number, type, or order of parameters) in the same scope.
-
Signature: Function name + parameter types (return type not part of signature).
-
Rules:
-
Must differ in parameter list.
-
Return type alone cannot differentiate overloaded functions.
-
constqualifier on member functions can differentiate.
-
-
Example:
int Max(int a, int b) { return (a>b)?a:b; } double Max(double a, double b) { return (a>b)?a:b; } string Max(string a, string b) { return (a>b)?a:b; }
Parameter Passing Mechanisms
-
Call by Value: Function receives a copy of the argument. Changes to parameter do not affect original. Safe but incurs copy overhead for large objects.
void func(int x) { x = 10; } // Original unchanged -
Call by Reference: Function receives a reference (alias) to the original argument. Changes affect original. Efficient, avoids copy.
void func(int &x) { x = 10; } // Original modified -
Return by Reference: Function returns a reference to a variable (usually a member or parameter). Allows chaining (
a = b = c). Must not return reference to local variable (dangling reference).class Array { int arr[10]; public: int& operator[](int i) { return arr[i]; } // Returns reference };
Operator Overloading
-
Syntax:
return_type operator symbol (parameter_list)Complex operator+(const Complex& rhs) const; -
Restrictions:
-
Cannot create new operators (only overload existing ones).
-
Cannot change operator precedence/associativity.
-
Cannot overload operators for built-in types only (at least one operand must be user-defined type).
-
::(scope),.*(pointer-to-member),?:(ternary),sizeofcannot be overloaded.
-
-
Unary Operator Overloading (e.g.,
-,++):-
As member function: 0 arguments (implicit
this). -
As friend function: 1 argument.
// Prefix ++ as member Complex& operator++() { /* increment this */ return *this; } -
-
Binary Operator Overloading (e.g.,
+,-):-
As member function: 1 argument (right-hand operand).
-
As friend function: 2 arguments.
// + as friend for Complex friend Complex operator+(const Complex& lhs, const Complex& rhs); -
-
Pros: Makes user-defined types behave like built-in types, improves readability.
-
Cons: Can lead to confusion if misused (e.g., overloading
,operator). Should maintain natural semantics.
4. Inheritance
Concept and Importance
-
Concept: Mechanism where a new class (derived/child) acquires properties and behaviors of an existing class (base/parent).
-
Importance:
-
Code Reusability: Avoid rewriting common attributes/methods.
-
Extensibility: Easily add new features to existing classes.
-
Logical Hierarchy: Models "is-a" relationships (e.g.,
Dogis-aAnimal). -
Achieves Polymorphism: Base class pointer/reference can point to derived objects.
-
Types of Inheritance
| Type | Description | Example | Diamond Problem? |
|---|---|---|---|
| Single | One derived from one base. | Car inherits Vehicle. |
No |
| Multilevel | Chain: Base → Intermediate → Derived. | Vehicle → Car → SportsCar. |
No |
| Multiple | One derived from multiple bases. | FlyingCar inherits Car and Aircraft. |
Yes |
| Hierarchical | Multiple derived from one base. | Car, Bike inherit Vehicle. |
No |
| Hybrid | Combination of two or more types. | FlyingCar (multiple) → SportsCar (multilevel). |
Possible |
Multiple Inheritance & Diamond Problem
-
Diamond Problem: Occurs when a class inherits from two classes that themselves share a common base. Derived class gets two copies of the common base members → ambiguity.
class A { public: void foo(); }; class B : public A {}; class C : public A {}; class D : public B, public C {}; // D has TWO A subobjects! D d; d.foo(); // ERROR: Ambiguous (B::A::foo or C::A::foo?) -
Resolution using Virtual Base Classes:
-
Declare base class as
virtualin intermediate classes. -
Ensures only one shared instance of the virtual base exists in the final derived class.
class B : virtual public A {}; class C : virtual public A {}; class D : public B, public C {}; // D has only ONE A subobject d.foo(); // OK, unambiguous -
Constructors in Derived Classes
-
Order of Execution: Base class constructor → Member object constructors (in declaration order) → Derived class constructor.
-
Initializing Base Members: Derived constructor must explicitly call appropriate base constructor using member initializer list.
class Base { int x; public: Base(int i) : x(i) {} }; class Derived : public Base { int y; public: Derived(int i, int j) : Base(i), y(j) {} // Base(i) calls base constructor };If base has no default constructor, derived must call a parameterized base constructor.
Base Class Pointer/Reference to Derived Object
-
Upcasting: Assigning derived class object/pointer/reference to base class pointer/reference. Implicit and safe.
Base* bptr = new Derived(); // Upcasting Base& bref = derivedObj; -
Use: Enables runtime polymorphism (virtual functions). Base pointer can call overridden derived functions.
-
Limitation: Through base pointer, only base class members are directly accessible. Derived-specific members require downcasting (explicit, potentially unsafe).
5. Polymorphism
Compile-Time Polymorphism (Static Binding)
-
Mechanism: Function call resolved at compile time.
-
Techniques:
-
Function Overloading: Same function name, different parameters.
-
Operator Overloading: Same operator symbol, different operand types.
-
Templates: Generic code works for multiple types (function/class templates).
-
-
Example:
Max(int, int),Max(double, double)– compiler decides which to call based on argument types.
Runtime Polymorphism (Dynamic Binding)
-
Mechanism: Function call resolved at runtime based on actual object type.
-
Key Requirement: Use base class pointer/reference to call a
virtualfunction. -
Virtual Functions:
-
Declared with
virtualkeyword in base class. -
Overridden in derived class with same signature.
-
Enables late binding: vTable (virtual table) mechanism stores function pointers. Object contains vPtr to its class's vTable.
class Shape { public: virtual double area() { return 0; } // Virtual }; class Circle : public Shape { double r; public: double area() override { return 3.14*r*r; } // Override }; Shape* s = new Circle(); cout << s->area(); // Calls Circle::area() at runtime -
-
Pure Virtual Functions:
-
Declared as
virtual returnType func() = 0; -
Makes the class abstract (cannot be instantiated).
-
Derived classes must override pure virtual functions to become concrete.
class Shape { public: virtual double area() = 0; // Pure virtual }; -
-
Abstract vs. Non-Abstract Classes:
| Abstract Class | Non-Abstract (Concrete) Class | | :--- | :--- | | Contains at least one pure virtual function. | No pure virtual functions. | | Cannot instantiate objects. | Can instantiate objects. | | Serves as base/inheritance contract. | Can be used directly or as base. | | Example:
Shape(witharea()=0). | Example:Circle(implementsarea()). |
Achieving Polymorphism: Comparison
| Aspect | Compile-Time | Runtime |
|---|---|---|
| Binding Time | Compile time | Runtime |
| Mechanism | Overloading, Templates | Virtual functions, Pointers/Refs |
| Speed | Faster (no lookup) | Slightly slower (vTable lookup) |
| Flexibility | Less flexible (fixed at compile) | More flexible (decides based on actual object) |
| Example | cout << max(10, 20); |
Shape* s = new Circle(); s->draw(); |
Example: Runtime Polymorphism (Area Calculation)
#include <iostream>
using namespace std;
class Shape {
public:
virtual double area() = 0; // Pure virtual
virtual ~Shape() {} // Virtual destructor
};
class Rectangle : public Shape {
double l, b;
public:
Rectangle(double x, double y) : l(x), b(y) {}
double area() override { return l * b; }
};
class Circle : public Shape {
double r;
public:
Circle(double x) : r(x) {}
double area() override { return 3.14 * r * r; }
};
int main() {
Shape* shapes[2];
shapes[0] = new Rectangle(10, 5);
shapes[1] = new Circle(7);
for (int i = 0; i < 2; i++) {
cout << "Area: " << shapes[i]->area() << endl; // Late binding
delete shapes[i];
}
}
6. Templates and Generic Programming
Function Templates
-
Purpose: Write a single function that works with different data types.
-
Syntax:
template <typename T> // or class T T Max(T a, T b) { return (a > b) ? a : b; } -
Template Parameters: Placeholders (
T) for types. Can have multiple parameters (template <class T, class U>). -
Usage: Compiler generates specific function versions based on argument types.
cout << Max(10, 20); // T deduced as int cout << Max(1.5, 2.5); // T deduced as double -
Overloaded Function Templates:
-
Can have multiple function templates with same name but different parameter types/constraints.
-
Overload resolution picks the most specific template.
template <typename T> T Max(T a, T b) { /* ... */ } template <typename T, typename U> T Max(T a, U b) { /* ... */ } // Different signature -
Class Templates (Brief)
-
Purpose: Define a class where member types are parameterized.
-
Syntax:
template <class T> class Array { T* arr; int size; public: Array(int s) : size(s) { arr = new T[s]; } T& operator[](int i) { return arr[i]; } }; -
Instantiation:
Array<int> intArr(10);– createsArraywithT=int.
7. Exception Handling
Need for Exception Handling
-
Separate error-handling code from normal code, improving readability and maintainability.
-
Propagate errors up the call stack to a central handler.
-
Handle errors in a type-safe manner using objects.
Keywords
-
try: Block containing code that might throw an exception. -
throw: Used to raise an exception (throw an object/value).if (denom == 0) throw "Division by zero!"; -
catch: Block that handles a specific exception type.catch(const char* msg) { cout << msg; }
Synchronous vs. Asynchronous Exceptions
| Synchronous Exceptions | Asynchronous Exceptions |
|---|---|
| Occur due to program statements (e.g., divide by zero, out-of-range array access). | Occur due to external events (e.g., keyboard interrupt, disk failure). |
C++ handles only synchronous exceptions via try/catch. |
Not handled by standard C++ exception mechanism. |
| Predictable, within program control. | Unpredictable, outside program control. |
Multiple Catch Blocks & Rethrowing
try {
// code that may throw int, char*, or other
throw 42;
}
catch(int e) {
cout << "Integer exception: " << e << endl;
}
catch(const char* s) {
cout << "String exception: " << s << endl;
}
catch(...) { // Catch-all handler (must be last)
cout << "Unknown exception" << endl;
throw; // Rethrow current exception to caller
}
Custom Exception Classes
-
Create user-defined exception types by inheriting from
std::exception(or any base). -
Override
what()method to provide error message.
class MyException : public exception {
string msg;
public:
MyException(const string& m) : msg(m) {}
const char* what() const noexcept override {
return msg.c_str();
}
};
// Usage:
throw MyException("Invalid input!");
8. Memory Management
Dynamic Memory Allocation
-
newOperator:-
For single object:
ClassName* ptr = new ClassName(args); -
For array:
ClassName* arr = new ClassName[size]; -
Returns pointer to allocated memory. Calls constructor.
-
-
deleteOperator:-
For single object:
delete ptr; -
For array:
delete[] arr;(Must matchnew/new[]) -
Calls destructor before freeing memory.
-
-
Key: Every
new/new[]must have a correspondingdelete/delete[]to avoid memory leaks.
Memory Leaks and Dangling Pointers
-
Memory Leak: Allocated memory (
new) is never deallocated (delete). Program loses pointer to that memory → unrecoverable until program ends.void leak() { int* p = new int(10); // No delete p; -> LEAK } -
Dangling Pointer: Pointer points to memory that has been deallocated.
int* p = new int(10); delete p; *p = 20; // ERROR: p is dangling, memory invalid p = nullptr; // Fix: set to null after delete
Example: Clearing Memory Used by an Object
class Data {
int* buffer;
public:
Data(int size) { buffer = new int[size]; }
~Data() { delete[] buffer; } // Destructor cleans up
void clear() { delete[] buffer; buffer = nullptr; } // Manual clear
};
Debugging Pointers (Basic Techniques)
-
Initialize to
nullptr:int* p = nullptr;– safe to delete. -
Check before dereferencing:
if (p != nullptr) { *p = 10; } -
Set to
nullptrafterdelete: Prevents accidental reuse. -
Use smart pointers (C++11+):
std::unique_ptr,std::shared_ptrfor automatic memory management. -
Tools: Valgrind (Linux), AddressSanitizer, Visual Studio Debugger to detect leaks/invalid access.
9. File I/O and Practical Programming
File Streams
-
ofstream: Output file stream (write to file). -
ifstream: Input file stream (read from file). -
fstream: Both input and output.
Reading/Writing
-
Text Mode (default): Data formatted as human-readable text. Newline translation may occur.
ofstream fout("file.txt"); fout << "Hello" << 123; // Write text ifstream fin("file.txt"); string s; int n; fin >> s >> n; // Read text -
Binary Mode: Raw bytes, no formatting. Use
ios::binary.fstream file("data.bin", ios::in | ios::out | ios::binary); file.write((char*)&obj, sizeof(obj)); // Write object bytes file.read((char*)&obj, sizeof(obj)); // Read object bytes
Example Programs (Frequent in Nov 2022)
-
Student Marks Validation:
class Student { int marks; public: void setMarks(int m) { if (m >= 65 && m <= 100) marks = m; else throw "Invalid marks!"; } }; -
Grade Calculation:
char getGrade(double avg) { if (avg >= 80) return 'A'; else if (avg >= 65) return 'B'; else if (avg >= 50) return 'C'; else if (avg >= 40) return 'D'; else return 'E'; } -
Branch Fees using Array:
double fees[] = {150000, 120000, 100000}; // Index: 0=CSE, 1=IT, 2=ECE int branchCode; // Input cout << "Fees: " << fees[branchCode]; -
Complex Number Arithmetic (Operator Overloading):
class Complex { double r, i; public: Complex operator+(const Complex& c) const { return Complex(r+c.r, i+c.i); } Complex operator*(const Complex& c) const { return Complex(r*c.r - i*c.i, r*c.i + i*c.r); } }; -
Inheritance: SurveyOperator:
class Person { /* ... no default ctor ... */ }; class SurveyOperator : public Person { int numCalls; public: SurveyOperator(string name) : Person(name) {} // Explicit base call }; -
Polymorphic Telephone:
class Telephone { protected: string phoneType; public: virtual void Ring() { cout << "Ringing " << phoneType; } virtual ~Telephone() {} }; class ElectronicPhone : public Telephone { public: ElectronicPhone() { phoneType = "Digital"; } void Ring() override { cout << "Digital ring tone!"; } };
10. Additional C++ Features
Static Members
-
Static Variables: Shared among all objects of the class. Defined inside class, initialized outside.
class Counter { static int count; // Declaration public: Counter() { count++; } }; int Counter::count = 0; // Definition & initialization -
Static Functions: Can be called without object. Access only static members.
static void showCount() { cout << count; } Counter::showCount(); // Call via class
Namespaces (Brief)
-
Purpose: Partition global namespace, prevent name collisions.
-
Syntax:
namespace MyLib { int x; void func(); } // Use: MyLib::x = 10; using namespace MyLib; // Brings all names into scope
Input/Output Methods in C++
| Method | Description | Example |
|---|---|---|
cin / cout |
Stream-based, type-safe, extensible via overloading. Slower than C I/O. | cin >> x;<br>cout << "Value: " << x; |
scanf / printf |
C-style, format-string based, faster but less safe (no type checking). | scanf("%d", &x);<br>printf("%d", x); |
gets / puts |
C-style string I/O (unsafe gets deprecated). |
char s[100]; gets(s); |
Exam Tips & Common Pitfalls
- Constructor Order: Always base first, then derived. Remember member initializer lists.
- Copy Constructor: Needed when class has raw pointers. Implement deep copy to avoid double-free/corruption.
- Virtual Destructor: Always make base class destructor
virtualif you have any virtual functions. Otherwise, deleting derived object via base pointer causes undefined behavior (only base destructor called).
new/deleteMismatch:newpairs withdelete;new[]pairs withdelete[]. Mismatch = undefined behavior.
- Inline vs. Macro: Inline is type-safe, respects scope. Macros are blind text substitution – watch for side effects (
#define SQUARE(x) x*x→SQUARE(a++)expands toa++*a++).
- Diamond Problem: If you see multiple inheritance with a common base, think virtual inheritance.
- Pure Virtual Function: Makes class abstract. Must be overridden in concrete derived class.
- Static Members: Defined outside class. No
staticin definition.
- Operator Overloading: Keep semantics intuitive.
operator+should not modify operands (usually).operator=must handle self-assignment.
- Exception Safety: Resources allocated before
throwmust be cleaned up (use RAII – Resource Acquisition Is Initialization – with constructors/destructors).