Skip to content
IT-304 · Object Oriented Programming & Methodology/Quick Revision Short Notes

Object Oriented Programming & Methodology (IT-304) - Unit 3 Short Notes

UNIT 3: Object-Oriented Programming in C++


I. OOP Fundamentals & C++ Overview

Advantages of OOP Paradigm

  • Modularity: Code is organized into self-contained objects.

  • Reusability: Classes can be reused via inheritance.

  • Data Security: Encapsulation protects internal data.

  • Easy Maintenance: Changes in one object rarely affect others.

  • Scalability: Complex systems can be built from simpler objects.

Core Features of OOP (4 Pillars)

  1. Encapsulation: Bundling data (attributes) and functions (methods) that operate on that data into a single unit (class). Access is controlled via access specifiers.

  2. Inheritance: Mechanism where a new class (derived/child) acquires properties and behaviors of an existing class (base/parent). Promotes code reuse.

  3. Polymorphism: "Many forms." Ability of an object to take different forms.

    • Compile-time (Static): Function/Operator Overloading.

    • Run-time (Dynamic): Virtual Functions.

  4. Abstraction: Hiding implementation details, showing only essential features. Achieved via abstract classes/interfaces.

C++ vs C Language

Feature C Language C++ Language
Paradigm Procedural Multi-paradigm (Procedural + OOP)
Data Security No access control private, public, protected
Functions Standalone Member functions of classes
Inheritance Not supported Supported (Single, Multiple, etc.)
Polymorphism Not supported Supported (Virtual functions)
Input/Output printf(), scanf() cin, cout (stream-based)
Memory malloc(), calloc(), free() new, delete operators
Error Handling Error codes, setjmp/longjmp Exceptions (try, catch, throw)
Type Checking Less strict Strict (function prototyping)

C++ Specifics: Variable Declaration Anywhere

  • Significance: Variables can be declared at the point of their first use within any block { }.

  • Benefit: Improves code readability, reduces scope of variables (localizes them), and prevents accidental use before initialization.

    
    void func() {
    
        int x = 10; // Declaration at point of use
    
        // ... use x
    
        for (int i = 0; i < x; i++) { // i declared in for-loop scope
    
            // ...
    
        }
    
    }
    
    

II. Classes, Objects, and Access Control

Class vs Structure in C++

Feature class struct
Default Access private public
Primary Use For objects with data hiding & methods For passive data structures (POD)
Inheritance Can have base classes Can have base classes
Member Functions Can have constructors, destructors, methods Can have constructors, destructors, methods (since C++)

[!TIP] In modern C++, the only technical difference is the default access specifier. Use class for data abstraction/hiding, struct for simple data aggregates.

Access Specifiers

  • private: Accessible only within the class itself.

  • public: Accessible from anywhere.

  • protected: Accessible within the class and its derived classes.

Access Specifiers with Respect to Inheritance

Base Specifier Derived Class Access (if inherited publicly) Derived Class Access (if inherited protectedly) Derived Class Access (if inherited privately)
public public protected private
protected protected protected private
private Inaccessible Inaccessible Inaccessible

Mechanism of Accessing Members

  1. Inside main(): Create an object. Use dot operator (.).

    
    ClassName obj;
    
    obj.publicMember = 10;
    
    obj.publicFunction();
    
    
  2. Inside member function of same class: Direct access by name.

    
    void ClassName::func() {
    
        privateMember = 5; // Direct access
    
        publicMember = 10;
    
    }
    
    
  3. Inside member function of another class:

    • If member is public: Use object of the first class with ..

    • If member is private/protected: Not accessible unless friend relationship exists.

Constructors & Destructors

  • Constructor: Special member function with same name as class, no return type. Called automatically when an object is created. Purpose: Initialize object's data members.

  • Destructor: Special member function with ~ClassName, no return type, no parameters. Called automatically when object goes out of scope or is deleted. Purpose: Clean up resources (e.g., delete dynamic memory).

Types of Constructors:

  1. Default Constructor: Takes no arguments. ClassName();

  2. Parameterized Constructor: Takes arguments. ClassName(int a, int b);

  3. Copy Constructor: Takes reference to object of same class. ClassName(const ClassName &obj);

Copy Constructor & Its Use:

  • Purpose: Initialize a new object as a copy of an existing object.

  • When Called:

    1. Object initialization: ClassName obj2 = obj1;

    2. Passing object by value to a function.

    3. Returning object by value from a function.

  • Example Program:

    
    class Box {
    
        int length;
    
    public:
    
        Box(int l) : length(l) {} // Parameterized
    
        Box(const Box &b) { // Copy Constructor
    
            length = b.length;
    
            cout << "Copy Constructor called" << endl;
    
        }
    
    };
    
    int main() {
    
        Box b1(10); // Parameterized
    
        Box b2 = b1; // Copy Constructor called
    
        return 0;
    
    }
    
    

Constructor Overloading & Chaining:

  • Overloading: Multiple constructors with different parameters.

  • Chaining (C++11 onwards): One constructor calling another using member initializer list.

    
    class Demo {
    
        int a, b;
    
    public:
    
        Demo() : Demo(0, 0) { cout << "Default called\n"; } // Chains to 2-arg
    
        Demo(int x) : Demo(x, 0) { cout << "1-arg called\n"; } // Chains to 2-arg
    
        Demo(int x, int y) : a(x), b(y) { // Ultimate initializer
    
            cout << "2-arg called\n";
    
        }
    
    };
    
    

Object Management

Array of Objects:


class Student {

    int rollno;

public:

    void setRoll(int r) { rollno = r; }

    void display() { cout << rollno; }

};

int main() {

    Student s[3]; // Array of 3 Student objects

    for(int i=0; i<3; i++) s[i].setRoll(i+1);

    for(int i=0; i<3; i++) s[i].display();

    return 0;

}

Array of Objects using Pointers (Dynamic Allocation):


int n = 5;

Student *arr = new Student[n]; // Allocates array on heap

for(int i=0; i<n; i++) arr[i].setRoll(i+1);

// ... use arr

delete[] arr; // Crucial: delete[] for arrays


III. Inheritance

Inheritance: Concept & Importance

  • Concept: Creating new classes (derived) from existing ones (base). Derived inherits base's members.

  • Importance:

    • Code Reusability: Avoid rewriting common code.

    • Extensibility: Easily add new features to existing classes.

    • Logical Hierarchy: Models real-world "is-a" relationships (e.g., Dog is-a Animal).

    • Transitivity: If C inherits B, and B inherits A, then C also inherits A.

Types of Inheritance

Type Description Example
Single One derived from one base. class Derived : public Base
Multilevel Chain: Base -> Intermediate -> Derived. A -> B -> C
Multiple One derived from multiple bases. class Derived : public Base1, public Base2
Hierarchical One base, multiple derived classes. A -> B, A -> C
Hybrid Combination of two or more types. A -> B (Single), A -> C (Hierarchical)

Difference: Multilevel vs Multiple Inheritance

Aspect Multilevel Inheritance Multiple Inheritance
Base Classes One base per level. Chain structure. Two or more base classes for a single derived.
Complexity Simpler, linear hierarchy. High complexity (Diamond problem, ambiguity).
Example GrandParent -> Parent -> Child Child : public Father, public Mother

[!TIP] Why Multiple Inheritance Can Induce Complexity?

  1. Ambiguity: If two base classes have a member with same name, which one does derived access? (e.g., Father::name vs Mother::name).
  1. Diamond Problem: Derived inherits two copies of a common base (via two paths). Causes duplication and inconsistency.
  1. Constructor/Destructor Order: More complex initialization/destruction sequence.
  1. Increased Coupling: Tightly binds derived to multiple base implementations.

Constructor Behavior in Inheritance

Order of Constructor Calls:

  1. Base class constructor (of top-most base) is called first.

  2. Then, intermediate base class constructors (in order of inheritance declaration).

  3. Finally, derived class constructor is called.

Destructor order is exact reverse.

Example:


class A { public: A() { cout << "A ctor\n"; } };

class B : public A { public: B() { cout << "B ctor\n"; } };

class C : public B { public: C() { cout << "C ctor\n"; } };

// Output: A ctor -> B ctor -> C ctor

Initializing Base Class Members:

  • Use member initializer list in derived constructor.

  • If base has no default constructor, derived must explicitly call a base constructor in its initializer list.

    
    class Base {
    
        int x;
    
    public:
    
        Base(int a) : x(a) {} // No default ctor
    
    };
    
    class Derived : public Base {
    
        int y;
    
    public:
    
        Derived(int a, int b) : Base(a), y(b) {} // MUST call Base(a)
    
    };
    
    

Ambiguity in Multiple Inheritance & Diamond Problem

  • Problem: If two base classes (B1, B2) inherit from a common base (A), and a derived class (D) inherits from both B1 and B2, then D contains two copies of A's members (via B1::A and B2::A). This is ambiguous and wasteful.

    
        A
    
       / \
    
      B1 B2
    
       \ /
    
        D  <-- D has two A subobjects!
    
    
  • Resolution: Virtual Base Classes

    • Declare base class (A) as virtual when inherited by B1 and B2.

    • Ensures only one shared instance of A exists in D.

    
    class A { /* ... */ };
    
    class B1 : virtual public A { /* ... */ }; // virtual inheritance
    
    class B2 : virtual public A { /* ... */ };
    
    class D : public B1, public B2 { /* ... */ }; // D has only ONE A
    
    

IV. Polymorphism

Compile-Time Polymorphism (Static Binding)

  • Function Overloading: Multiple functions with same name but different parameters (type/number).

    
    void print(int i) { cout << i; }
    
    void print(double d) { cout << d; }
    
    void print(char* s) { cout << s; }
    
    
  • Operator Overloading: Redefining operator behavior for user-defined types.

    • Unary Operator Example (++ prefix):

      
      class Counter {
      
          int count;
      
      public:
      
          Counter& operator++() { // Prefix: ++obj
      
              ++count;
      
              return *this; // Return reference to modified object
      
          }
      
      };
      
      
    • Pros: Intuitive syntax for custom types (e.g., c1 + c2).

    • Cons: Can make code obscure if overused/abused; cannot change operator precedence/associativity.

Run-Time Polymorphism (Dynamic Binding)

  • Virtual Functions: Declared with virtual keyword in base class. Allows overriding in derived classes. Calls are resolved at runtime based on object's actual type.

    
    class Base {
    
    public:
    
        virtual void show() { cout << "Base\n"; }
    
    };
    
    class Derived : public Base {
    
    public:
    
        void show() override { cout << "Derived\n"; } // Override
    
    };
    
    int main() {
    
        Base *b = new Derived();
    
        b->show(); // Output: "Derived" (Late binding)
    
    }
    
    
  • Pure Virtual Function & Abstract Class:

    • virtual void func() = 0; (Pure virtual).

    • Class containing at least one pure virtual is abstract.

    • Cannot instantiate abstract class objects.

    • Purpose: Define interface/template for derived classes.

    
    class Shape {
    
    public:
    
        virtual double area() = 0; // Pure virtual
    
    };
    
    class Circle : public Shape {
    
        double r;
    
    public:
    
        double area() override { return 3.14*r*r; }
    
    };
    
    // Shape s; // ERROR: Abstract class
    
    Circle c; // OK
    
    

How Polymorphism is Achieved:

Type Mechanism Binding Time Example
Compile-time Overloading Early/Static (at compile) Function overloading, Operator overloading
Run-time Overriding via virtual Late/Dynamic (at runtime) Virtual functions, Pure virtual functions

Binding in Polymorphism:

  • Early Binding (Static): Function call linked to function definition at compile time. For non-virtual, overloaded functions.

  • Late Binding (Dynamic): Function call linked to function definition at runtime. For virtual functions. Requires vtable (virtual table) mechanism.


V. Advanced C++ Features

Friend Functions & Classes

  • Friend Function: Non-member function granted access to private/protected members of a class.

    • Declared with friend keyword inside the class.

    • Not a member of the class (cannot use this pointer).

    • Application: Operator overloading for symmetric operations (e.g., ostream &operator<<), utility functions needing deep access.

    
    class Box {
    
        int length;
    
    public:
    
        Box(int l) : length(l) {}
    
        friend void showLength(Box b); // Friend declaration
    
    };
    
    void showLength(Box b) { // Definition (not member)
    
        cout << b.length; // Access private member
    
    }
    
    

Static Members and Functions

  • Static Data Member: Shared by all objects of the class. One copy exists. Must be defined outside class.

    
    class Counter {
    
        static int count; // Declaration
    
    public:
    
        Counter() { count++; }
    
        static int getCount() { return count; }
    
    };
    
    int Counter::count = 0; // Definition & initialization
    
    
  • Static Member Function: Can access only static members of class. Called using class name (ClassName::func()), no this pointer.

Templates

  • Function Template: Blueprint for generating functions for different types.

    
    template <typename T>
    
    T max(T a, T b) {
    
        return (a > b) ? a : b;
    
    }
    
    // Instantiations: max<int>(1,2), max<double>(1.1, 2.2)
    
    
  • Overloaded Function Templates: Multiple templates with same name but different template parameters or function parameters.

    
    template <typename T>
    
    T max(T a, T b) { /* ... */ }
    
    template <typename T, typename U>
    
    auto max(T a, U b) -> decltype(a+b) { /* ... */ } // Different signature
    
    

Exception Handling

  • Keywords:

    • try: Block where exceptions might occur.

    • catch: Handler for specific exception type.

    • throw: Raises an exception.

    
    try {
    
        if (error) throw "Error message"; // Throw
    
    }
    
    catch (const char* e) { // Catch
    
        cout << "Caught: " << e;
    
    }
    
    
  • Synchronous vs Asynchronous Exceptions:

    | | Synchronous | Asynchronous | | :--- | :--- | :--- | | Cause | Directly from throw statement within try block. | External events (hardware failure, signals, interrupts). | | Handling | Handled by nearest matching catch. | Cannot be handled by C++ try-catch. Requires OS/hardware support. | | Example | throw -1; | Division by zero (hardware), SIGSEGV signal. |

Inline Functions

  • Purpose: Suggest compiler to replace function call with function body to avoid call overhead. Use for small, frequently called functions.

  • Syntax: Define function inside class definition or use inline keyword.

    
    class Calc {
    
    public:
    
        inline int add(int a, int b) { return a+b; } // Inline member
    
    };
    
    
  • Macros vs Inline Functions:

    | Feature | Macros (#define) | Inline Functions | | :--- | :--- | :--- | | Type Safety | No (textual substitution) | Yes (type-checked by compiler) | | Debugging | Difficult (no symbol) | Easier (symbol exists) | | Scope | Global (preprocessor) | Respects scope & access control | | Parameters | No evaluation safety (e.g., #define SQR(x) x*x) | Safe evaluation (arguments evaluated once) | | Return Type | No return type check | Has return type |


VI. Memory Management & I/O

Memory Management: new / delete

  • new: Allocates memory on heap, returns pointer. Calls constructor.

    
    int *p = new int(10); // Single int
    
    int *arr = new int[5]; // Array
    
    ClassName *obj = new ClassName(args); // Object
    
    
  • delete: Frees memory allocated by new. Calls destructor.

    
    delete p;        // Single object
    
    delete[] arr;    // Array (MUST use []!)
    
    delete obj;
    
    
  • Clearing Memory: After delete, set pointer to nullptr to avoid dangling pointer.

    
    p = nullptr; // Safe
    
    

Call by Reference & Return by Reference

  • Call by Reference (&): Function receives alias to original variable. Changes affect original. Avoids copy overhead for large objects.

    
    void increment(int &x) { x++; } // Reference parameter
    
    int main() {
    
        int a = 5;
    
        increment(a); // a becomes 6
    
    }
    
    
  • Return by Reference: Function returns reference to a variable (usually a class member). Must not return reference to local variable (dangling reference).

    
    class Array {
    
        int data[100];
    
    public:
    
        int& get(int index) { return data[index]; } // Returns reference
    
    };
    
    int main() {
    
        Array arr;
    
        arr.get(0) = 100; // Direct assignment via returned reference
    
    }
    
    

Input/Output in C++

  1. cin / cout (Stream-based - Preferred):

    
    int x; string s;
    
    cin >> x >> s; // Formatted input
    
    cout << "Value: " << x; // Formatted output
    
    
  2. scanf / printf (C-style - Still supported):

    
    int x;
    
    scanf("%d", &x); // Requires address-of operator (&)
    
    printf("%d", x);
    
    

[!TIP] cin/cout are type-safe, extensible (via overloading), and integrate with C++ I/O library (fstream). scanf/printf are faster but error-prone (format specifier mismatch).

File I/O (fstream):

#include <fstream>

int main() {

    // Writing

    ofstream outFile("data.txt");

    outFile << "Hello File";

    outFile.close();

    // Reading

    ifstream inFile("data.txt");

    string line;

    getline(inFile, line);

    inFile.close();

    return 0;

}

Pointer Debugging

  • Dangling Pointer: Pointer pointing to deleted/invalid memory.

    
    int *p = new int(5);
    
    delete p;
    
    // p is now dangling (points to freed memory)
    
    *p = 10; // ERROR: Undefined behavior!
    
    
  • Memory Leak: Allocated memory (new) never deallocated (delete). Lost pointer to block.

    
    void leak() {
    
        int *p = new int[100];
    
        // No delete[] p; -> Memory leak on function exit
    
    }
    
    
  • Debugging Tips:

    1. Initialize pointers to nullptr.

    2. After delete, set pointer to nullptr.

    3. Use smart pointers (unique_ptr, shared_ptr) for automatic management.

    4. Tools: Valgrind (Linux), AddressSanitizer.


VII. Practical Programming Applications

Validation of Examination Marks


class Student {

    int marks;

public:

    void setMarks(int m) {

        if (m >= 65 && m <= 100) marks = m;

        else { cout << "Invalid marks!\n"; marks = 0; }

    }

    int getMarks() { return marks; }

};

Grade Calculation System


class Student {

    int m1, m2, m3;

public:

    void setMarks(int a, int b, int c) { m1=a; m2=b; m3=c; }

    void getResults() {

        double avg = (m1+m2+m3)/3.0;

        char grade;

        if (avg >= 80) grade = 'A';

        else if (avg >= 65) grade = 'B';

        else if (avg >= 50) grade = 'C';

        else if (avg >= 40) grade = 'D';

        else grade = 'E';

        cout << "Grade: " << grade << endl;

    }

};

Complex Number Arithmetic


class Complex {

    double real, imag;

public:

    Complex(double r=0, double i=0) : real(r), imag(i) {}

    Complex operator+(const Complex& c) {

        return Complex(real + c.real, imag + c.imag);

    }

    Complex operator-(const Complex& c) {

        return Complex(real - c.real, imag - c.imag);

    }

    Complex operator*(const Complex& c) {

        return Complex(

            real*c.real - imag*c.imag,

            real*c.imag + imag*c.real

        );

    }

    void display() { cout << real << " + " << imag << "i\n"; }

};

Inheritance-Based Designs

Employee (Derived from Person):


class Person {

    string name;

public:

    Person(string n) : name(n) {}

    string getName() { return name; }

};

class Employee : public Person {

    double salary;

    int year;

    string insuranceNo;

public:

    Employee(string n, double s, int y, string ins)

        : Person(n), salary(s), year(y), insuranceNo(ins) {}

    bool equals(Employee& e) {

        return getName() == e.getName() && insuranceNo == e.insuranceNo;

    }

};

SurveyOperator (Derived from Person):


class SurveyOperator : public Person {

    int numCalls, successCalls;

    double totalTime;

    bool available;

public:

    SurveyOperator(string name, int nc, int sc, double t, bool av)

        : Person(name), numCalls(nc), successCalls(sc), totalTime(t), available(av) {}

    // ... other methods (GetNumberOfCalls, SetAvailable, MakeCall)

};

Array-Based Fee Calculation


int main() {

    int branchId[] = {1, 2, 1, 3, 2}; // Example branch IDs

    double fees[] = {50000, 60000, 70000}; // Index: branchId-1

    double total = 0;

    for (int id : branchId) {

        total += fees[id-1];

    }

    cout << "Total Fees: " << total;

    return 0;

}

VIII. OOP Design Concepts

Encapsulation vs Information Hiding

Encapsulation Information Hiding
Bundling data & methods into a single unit (class). Hiding internal implementation details, exposing only interface.
How: Using class/struct. How: Using private/protected members, public interface.
Focus: Organization. Focus: Protection & abstraction.
Example: All members in one class. Example: private data, public getter/setter methods.

OOP Techniques for Handling System Complexity

  1. Abstraction: Model essential features, ignore irrelevant details.

  2. Encapsulation: Contain complexity within well-defined boundaries.

  3. Modularity: Break system into manageable, independent modules (classes).

  4. Hierarchy (Inheritance): Organize classes into logical, reusable hierarchies.

  5. Separation of Interface & Implementation: Users interact with interface, not implementation.

Overloading vs Overriding

Feature Overloading Overriding
Definition Same function name, different parameters in same class. Same function signature in base & derived classes.
Scope Single class (or namespace). Inheritance hierarchy (base & derived).
Binding Early/Static binding (compile-time). Late/Dynamic binding (runtime) if virtual.
Purpose Provide multiple ways to call similar operation. Change/specialize base class behavior in derived.
Example void print(int); void print(double); virtual void show(); in base, void show() override; in derived

[!TIP] Binding in Polymorphism:

  • Overloading: Compiler decides which function to call based on argument types at compile time.
  • Overriding (with virtual): Runtime decides which function to call based on object's actual type using vtable.
Go to where you left off?

Quick Add to Notes

Save questions, your own notes and screenshots into notes filed by unit. It takes a free account.

Create free account

Have an account? Log in