C++ OOP Basics Object-Oriented Programming
Four Pillars

C++ Object-Oriented Programming: Complete Guide

Master C++ OOP fundamentals: classes, objects, inheritance, polymorphism, encapsulation, and abstraction with real-world examples and best practices.

Encapsulation

Data hiding and protection

Inheritance

Code reusability and hierarchy

Polymorphism

Many forms, single interface

Abstraction

Hide complexity, show essentials

Introduction to Object-Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around data, or objects, rather than functions and logic. C++ is a multi-paradigm language that fully supports OOP.

Real-World Analogy

Think of a Car as a class. Each individual car (Toyota Camry, Honda Civic) is an object. All cars inherit basic properties from the Vehicle class. Different cars (electric, gasoline) implement the same interface (start(), stop()) differently - that's polymorphism!

Why Use OOP?
  • Modularity for easier maintenance
  • Code reusability through inheritance
  • Data hiding and security
  • Flexibility through polymorphism
  • Better problem-solving approach
  • Easier collaboration in teams
OOP vs Procedural Programming
  • Procedural: Focus on functions
  • OOP: Focus on data and objects
  • Procedural: Data is global or passed
  • OOP: Data is encapsulated in objects
  • Procedural: Harder to maintain
  • OOP: Easier to scale and maintain

C++ OOP Concepts Overview

The following table explains the fundamental concepts of Object-Oriented Programming in C++:

Concept Definition C++ Implementation Real-World Example
Class Blueprint or template for creating objects class ClassName { }; Car design blueprint
Object Instance of a class with actual data ClassName obj; Actual car on the road
Encapsulation Bundling data and methods together, hiding implementation private: and public: Capsule containing medicine
Inheritance Deriving new classes from existing ones class Child : public Parent Child inherits traits from parents
Polymorphism One interface, multiple implementations virtual functions, overriding Person can be Student, Teacher, etc.
Abstraction Hiding complex reality while exposing essentials Abstract classes, interfaces Car dashboard (hides engine complexity)
Constructor Special method called when object is created ClassName() { } Car assembly process
Destructor Special method called when object is destroyed ~ClassName() { } Car recycling process

1. Classes and Objects: The Foundation

A class is a user-defined data type that holds both data (attributes) and functions (methods). An object is an instance of a class.

Class Definition Syntax
class ClassName {
private:
    // Private members (accessible only within class)
    dataType privateVariable;
    
protected:
    // Protected members (accessible within class and derived classes)
    dataType protectedVariable;
    
public:
    // Public members (accessible from anywhere)
    dataType publicVariable;
    
    // Constructor
    ClassName(parameters) {
        // Initialization code
    }
    
    // Member functions (methods)
    returnType methodName(parameters) {
        // Method implementation
    }
    
    // Destructor
    ~ClassName() {
        // Cleanup code
    }
};
Complete Class and Object Example: Bank Account System
#include <iostream>
#include <string>
using namespace std;

// ======================
// CLASS DECLARATION
// ======================
class BankAccount {
private:
    // Private data members (encapsulation)
    string accountNumber;
    string accountHolder;
    double balance;
    
public:
    // ======================
    // CONSTRUCTORS
    // ======================
    
    // Default constructor
    BankAccount() {
        accountNumber = "000000";
        accountHolder = "Unknown";
        balance = 0.0;
        cout << "Default constructor called" << endl;
    }
    
    // Parameterized constructor
    BankAccount(string accNum, string holder, double initialBalance) {
        accountNumber = accNum;
        accountHolder = holder;
        
        if (initialBalance >= 0) {
            balance = initialBalance;
        } else {
            balance = 0.0;
            cout << "Warning: Negative balance set to 0" << endl;
        }
        cout << "Parameterized constructor called for " << holder << endl;
    }
    
    // Copy constructor
    BankAccount(const BankAccount& other) {
        accountNumber = other.accountNumber + "-COPY";
        accountHolder = other.accountHolder + " (Copy)";
        balance = other.balance;
        cout << "Copy constructor called" << endl;
    }
    
    // ======================
    // DESTRUCTOR
    // ======================
    ~BankAccount() {
        cout << "Destructor called for " << accountHolder << endl;
    }
    
    // ======================
    // PUBLIC MEMBER FUNCTIONS (Interface)
    // ======================
    
    // Getter methods (accessors)
    string getAccountNumber() const {
        return accountNumber;
    }
    
    string getAccountHolder() const {
        return accountHolder;
    }
    
    double getBalance() const {
        return balance;
    }
    
    // Setter methods (mutators)
    void setAccountHolder(string newHolder) {
        if (!newHolder.empty()) {
            accountHolder = newHolder;
        }
    }
    
    // Business logic methods
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            cout << "Deposited: $" << amount << endl;
            cout << "New balance: $" << balance << endl;
        } else {
            cout << "Invalid deposit amount!" << endl;
        }
    }
    
    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            cout << "Withdrawn: $" << amount << endl;
            cout << "New balance: $" << balance << endl;
            return true;
        } else {
            cout << "Insufficient funds or invalid amount!" << endl;
            return false;
        }
    }
    
    void displayAccountInfo() const {
        cout << "\n=== Account Information ===" << endl;
        cout << "Account Number: " << accountNumber << endl;
        cout << "Account Holder: " << accountHolder << endl;
        cout << "Balance: $" << balance << endl;
        cout << "===========================\n" << endl;
    }
};

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== BANK ACCOUNT SYSTEM DEMONSTRATION ===" << endl << endl;
    
    // ======================
    // CREATING OBJECTS
    // ======================
    
    // Method 1: Default constructor
    cout << "1. Creating account with default constructor:" << endl;
    BankAccount account1;  // Default constructor called
    account1.displayAccountInfo();
    
    // Method 2: Parameterized constructor
    cout << "2. Creating account with parameterized constructor:" << endl;
    BankAccount account2("123456", "John Doe", 1000.00);
    account2.displayAccountInfo();
    
    // Method 3: Copy constructor
    cout << "3. Creating account using copy constructor:" << endl;
    BankAccount account3 = account2;  // Copy constructor called
    account3.displayAccountInfo();
    
    cout << "===========================================" << endl << endl;
    
    // ======================
    // USING OBJECT METHODS
    // ======================
    
    cout << "4. Performing transactions on John's account:" << endl;
    
    // Deposit money
    cout << "\nDepositing $500..." << endl;
    account2.deposit(500.00);
    
    // Withdraw money
    cout << "\nWithdrawing $200..." << endl;
    account2.withdraw(200.00);
    
    // Try to withdraw too much
    cout << "\nTrying to withdraw $2000..." << endl;
    account2.withdraw(2000.00);
    
    // Display updated info
    account2.displayAccountInfo();
    
    cout << "===========================================" << endl << endl;
    
    // ======================
    // ACCESSING MEMBERS
    // ======================
    
    cout << "5. Accessing account information using getters:" << endl;
    cout << "Account Number: " << account2.getAccountNumber() << endl;
    cout << "Account Holder: " << account2.getAccountHolder() << endl;
    cout << "Balance: $" << account2.getBalance() << endl;
    
    cout << "\n6. Changing account holder name:" << endl;
    account2.setAccountHolder("John Smith");
    cout << "New account holder: " << account2.getAccountHolder() << endl;
    
    cout << "===========================================" << endl << endl;
    
    // ======================
    // ARRAY OF OBJECTS
    // ======================
    
    cout << "7. Creating array of BankAccount objects:" << endl;
    BankAccount accounts[3] = {
        BankAccount("111111", "Alice", 500.00),
        BankAccount("222222", "Bob", 1500.00),
        BankAccount("333333", "Charlie", 2500.00)
    };
    
    // Display all accounts
    for (int i = 0; i < 3; i++) {
        accounts[i].displayAccountInfo();
    }
    
    cout << "===========================================" << endl << endl;
    
    // ======================
    // POINTERS TO OBJECTS
    // ======================
    
    cout << "8. Using pointers with objects:" << endl;
    
    // Creating object using pointer
    BankAccount* ptrAccount = new BankAccount("444444", "David", 3000.00);
    
    // Accessing members through pointer
    cout << "Account holder (via pointer): " << ptrAccount->getAccountHolder() << endl;
    
    // Calling methods through pointer
    ptrAccount->deposit(1000.00);
    
    // Don't forget to delete!
    delete ptrAccount;
    
    cout << "===========================================" << endl << endl;
    
    // ======================
    // CONST OBJECTS
    // ======================
    
    cout << "9. Working with const objects:" << endl;
    const BankAccount constAccount("555555", "Eve", 4000.00);
    
    // Can only call const member functions on const objects
    cout << "Const account balance: $" << constAccount.getBalance() << endl;
    constAccount.displayAccountInfo();
    
    // This would cause compilation error:
    // constAccount.deposit(100.00);  // Error: deposit() is not const
    
    cout << "===========================================" << endl << endl;
    
    // Destructors will be called automatically when objects go out of scope
    cout << "Program ending. Destructors will be called automatically..." << endl;
    
    return 0;
}
Class Design Best Practices:
  • Use meaningful, descriptive class names
  • Keep data members private (encapsulation)
  • Provide public getter/setter methods if needed
  • Initialize all data members in constructors
  • Follow the Rule of Three/Five for resource management
  • Make member functions const when they don't modify object
Common Mistakes:
  • Forgetting semicolon after class definition
  • Making all data members public (breaks encapsulation)
  • Not initializing pointers in constructors
  • Memory leaks (not deleting dynamic memory)
  • Shallow copying when deep copy is needed
  • Not making destructor virtual in base class
BankAccount
- accountNumber: string
- accountHolder: string
- balance: double
+ BankAccount()
+ BankAccount(string, string, double)
+ ~BankAccount()
+ getAccountNumber(): string
+ getAccountHolder(): string
+ getBalance(): double
+ setAccountHolder(string): void
+ deposit(double): void
+ withdraw(double): bool
+ displayAccountInfo(): void

2. Constructors and Destructors

Constructors initialize objects when they are created. Destructors clean up when objects are destroyed. C++ provides several types of constructors.

Default Constructor
  • No parameters
  • Called when: ClassName obj;
  • Initializes with default values
  • Compiler provides if no constructors defined
Parameterized Constructor
  • Accepts parameters
  • Called when: ClassName obj(params);
  • Initializes with provided values
  • Allows custom initialization
Copy Constructor
  • Creates copy of existing object
  • Called when: ClassName obj2 = obj1;
  • Deep vs shallow copy
  • Compiler provides default if not defined
Constructor and Destructor Examples
#include <iostream>
#include <string>
#include <cstring> // For strlen, strcpy
using namespace std;

// ======================
// 1. SIMPLE CONSTRUCTOR/DESTRUCTOR
// ======================
class SimpleClass {
private:
    int id;
    string name;
    
public:
    // Default constructor
    SimpleClass() {
        id = 0;
        name = "Default";
        cout << "Default constructor called" << endl;
    }
    
    // Parameterized constructor
    SimpleClass(int i, string n) {
        id = i;
        name = n;
        cout << "Parameterized constructor called for " << name << endl;
    }
    
    // Copy constructor
    SimpleClass(const SimpleClass& other) {
        id = other.id;
        name = other.name + " (Copy)";
        cout << "Copy constructor called" << endl;
    }
    
    // Destructor
    ~SimpleClass() {
        cout << "Destructor called for " << name << endl;
    }
    
    void display() {
        cout << "ID: " << id << ", Name: " << name << endl;
    }
};

// ======================
// 2. DYNAMIC MEMORY MANAGEMENT
// ======================
class DynamicClass {
private:
    char* buffer;
    int size;
    
public:
    // Constructor with dynamic allocation
    DynamicClass(const char* str) {
        size = strlen(str);
        buffer = new char[size + 1];
        strcpy(buffer, str);
        cout << "DynamicClass created: " << buffer << endl;
    }
    
    // Copy constructor (DEEP COPY)
    DynamicClass(const DynamicClass& other) {
        size = other.size;
        buffer = new char[size + 1];
        strcpy(buffer, other.buffer);
        cout << "Deep copy created: " << buffer << endl;
    }
    
    // Destructor
    ~DynamicClass() {
        cout << "DynamicClass destroyed: " << buffer << endl;
        delete[] buffer; // CRITICAL: Free allocated memory
    }
    
    void display() {
        cout << "Content: " << buffer << " (Size: " << size << ")" << endl;
    }
};

// ======================
// 3. CONSTRUCTOR OVERLOADING
// ======================
class Rectangle {
private:
    double length;
    double width;
    
public:
    // Constructor 1: No parameters
    Rectangle() {
        length = 1.0;
        width = 1.0;
        cout << "Default rectangle created (1x1)" << endl;
    }
    
    // Constructor 2: One parameter (square)
    Rectangle(double side) {
        length = side;
        width = side;
        cout << "Square created: " << side << "x" << side << endl;
    }
    
    // Constructor 3: Two parameters
    Rectangle(double l, double w) {
        length = l;
        width = w;
        cout << "Rectangle created: " << l << "x" << w << endl;
    }
    
    // Constructor 4: Using initializer list (preferred)
    Rectangle(int l, int w) : length(l), width(w) {
        cout << "Rectangle created with initializer list: " 
             << l << "x" << w << endl;
    }
    
    double area() {
        return length * width;
    }
    
    void display() {
        cout << "Length: " << length << ", Width: " << width 
             << ", Area: " << area() << endl;
    }
};

// ======================
// 4. DESTRUCTOR DEMONSTRATION
// ======================
class ResourceHolder {
private:
    int* data;
    int size;
    
public:
    // Constructor
    ResourceHolder(int s) : size(s) {
        data = new int[size];
        for (int i = 0; i < size; i++) {
            data[i] = i * 10;
        }
        cout << "ResourceHolder created with " << size << " elements" << endl;
    }
    
    // DESTRUCTOR - Cleans up dynamically allocated memory
    ~ResourceHolder() {
        cout << "ResourceHolder destroyed. Freeing " << size << " elements" << endl;
        delete[] data; // Prevents memory leak
    }
    
    void display() {
        cout << "Data: ";
        for (int i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
};

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== CONSTRUCTORS AND DESTRUCTORS DEMONSTRATION ===" << endl << endl;
    
    // ======================
    // 1. BASIC CONSTRUCTORS
    // ======================
    cout << "1. Basic Constructors:" << endl;
    {
        cout << "\nEntering block scope..." << endl;
        
        SimpleClass obj1;                    // Default constructor
        SimpleClass obj2(101, "Alice");      // Parameterized constructor
        SimpleClass obj3 = obj2;             // Copy constructor
        
        obj1.display();
        obj2.display();
        obj3.display();
        
        cout << "Exiting block scope..." << endl;
    } // Destructors called here automatically
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // 2. DYNAMIC MEMORY
    // ======================
    cout << "2. Dynamic Memory Management:" << endl;
    {
        DynamicClass dyn1("Hello World");
        DynamicClass dyn2 = dyn1;  // Deep copy
        
        dyn1.display();
        dyn2.display();
        
        // Note: No need to manually delete - destructor handles it!
    }
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // 3. CONSTRUCTOR OVERLOADING
    // ======================
    cout << "3. Constructor Overloading:" << endl;
    
    Rectangle rect1;            // Calls default constructor
    Rectangle rect2(5.0);       // Calls one-parameter constructor
    Rectangle rect3(4.0, 6.0);  // Calls two-parameter constructor
    Rectangle rect4(3, 7);      // Calls constructor with initializer list
    
    rect1.display();
    rect2.display();
    rect3.display();
    rect4.display();
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // 4. DESTRUCTOR IMPORTANCE
    // ======================
    cout << "4. Destructor Importance:" << endl;
    
    {
        cout << "\nCreating ResourceHolder in inner scope..." << endl;
        ResourceHolder rh(5);
        rh.display();
        cout << "Leaving inner scope..." << endl;
    } // Destructor automatically called here
    
    cout << "\nMemory was automatically freed by destructor!" << endl;
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // 5. ARRAY OF OBJECTS
    // ======================
    cout << "5. Array of Objects:" << endl;
    
    SimpleClass objects[3] = {
        SimpleClass(),              // Default constructor
        SimpleClass(201, "Bob"),    // Parameterized constructor
        SimpleClass(202, "Charlie") // Parameterized constructor
    };
    
    for (int i = 0; i < 3; i++) {
        objects[i].display();
    }
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // 6. POINTERS AND NEW/DELETE
    // ======================
    cout << "6. Pointers and Dynamic Objects:" << endl;
    
    // Creating object with new
    SimpleClass* ptr = new SimpleClass(301, "David");
    ptr->display();
    
    // Must delete manually when using new
    delete ptr;  // Calls destructor
    
    cout << "\n===========================================" << endl << endl;
    
    cout << "Key Takeaways:" << endl;
    cout << "1. Constructors initialize objects" << endl;
    cout << "2. Destructors clean up resources" << endl;
    cout << "3. Always free dynamic memory in destructor" << endl;
    cout << "4. Use initializer lists for efficiency" << endl;
    cout << "5. Copy constructor should do deep copy for pointers" << endl;
    
    return 0;
}
Constructor Best Practices:
  • Use initializer lists for member initialization (more efficient)
  • Provide a default constructor if objects might be created without arguments
  • Make copy constructor do deep copy when class has pointer members
  • Follow the Rule of Three: If you need destructor, copy constructor, or copy assignment, you likely need all three
  • Use delegating constructors (C++11) to avoid code duplication
  • Initialize all data members in every constructor

3. Encapsulation: Data Hiding and Protection

Encapsulation is the bundling of data and methods that operate on that data within a single unit (class), and restricting direct access to some of the object's components.

Access Specifiers
class Example {
private:    // Accessible ONLY within this class
    int secretData;
    
protected:  // Accessible within this class AND derived classes
    int familyData;
    
public:     // Accessible from ANYWHERE
    int publicData;
    
    // Getter method (accessor)
    int getSecretData() const {
        return secretData;
    }
    
    // Setter method (mutator) with validation
    void setSecretData(int value) {
        if (value >= 0) {
            secretData = value;
        }
    }
};
Encapsulation Examples: Student Management System
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// ======================
// 1. STUDENT CLASS WITH ENCAPSULATION
// ======================
class Student {
private:
    // PRIVATE data members - cannot be accessed directly from outside
    string name;
    int age;
    string studentID;
    vector<double> grades;
    static int totalStudents; // Static member
    
    // Private helper method - internal use only
    double calculateAverage() const {
        if (grades.empty()) return 0.0;
        
        double sum = 0.0;
        for (double grade : grades) {
            sum += grade;
        }
        return sum / grades.size();
    }
    
public:
    // Constructor
    Student(string n, int a, string id) : name(n), age(a), studentID(id) {
        totalStudents++;
        cout << "Student " << name << " created. Total students: " 
             << totalStudents << endl;
    }
    
    // Destructor
    ~Student() {
        totalStudents--;
        cout << "Student " << name << " removed. Total students: " 
             << totalStudents << endl;
    }
    
    // ======================
    // PUBLIC INTERFACE (Getters and Setters)
    // ======================
    
    // Getter for name (read-only access)
    string getName() const {
        return name;
    }
    
    // Getter for age
    int getAge() const {
        return age;
    }
    
    // Setter for age with validation
    void setAge(int newAge) {
        if (newAge >= 0 && newAge <= 120) {
            age = newAge;
            cout << "Age updated to " << age << endl;
        } else {
            cout << "Invalid age! Age must be between 0 and 120." << endl;
        }
    }
    
    // Getter for studentID (read-only)
    string getStudentID() const {
        return studentID;
    }
    
    // Method to add grade with validation
    void addGrade(double grade) {
        if (grade >= 0.0 && grade <= 100.0) {
            grades.push_back(grade);
            cout << "Grade " << grade << " added for " << name << endl;
        } else {
            cout << "Invalid grade! Grade must be between 0 and 100." << endl;
        }
    }
    
    // Get grades (returns copy for encapsulation)
    vector<double> getGrades() const {
        return grades; // Returns copy, not reference
    }
    
    // Get average grade (calls private helper)
    double getAverageGrade() const {
        return calculateAverage();
    }
    
    // Get letter grade based on average
    char getLetterGrade() const {
        double avg = getAverageGrade();
        
        if (avg >= 90.0) return 'A';
        else if (avg >= 80.0) return 'B';
        else if (avg >= 70.0) return 'C';
        else if (avg >= 60.0) return 'D';
        else return 'F';
    }
    
    // Display student information
    void displayInfo() const {
        cout << "\n=== Student Information ===" << endl;
        cout << "Name: " << name << endl;
        cout << "Age: " << age << endl;
        cout << "Student ID: " << studentID << endl;
        cout << "Number of grades: " << grades.size() << endl;
        
        if (!grades.empty()) {
            cout << "Grades: ";
            for (double grade : grades) {
                cout << grade << " ";
            }
            cout << endl;
            cout << "Average: " << getAverageGrade() << endl;
            cout << "Letter Grade: " << getLetterGrade() << endl;
        }
        cout << "===========================\n" << endl;
    }
    
    // Static method to get total students
    static int getTotalStudents() {
        return totalStudents;
    }
};

// Initialize static member
int Student::totalStudents = 0;

// ======================
// 2. COURSE CLASS DEMONSTRATING ENCAPSULATION
// ======================
class Course {
private:
    string courseName;
    string courseCode;
    int maxStudents;
    vector<Student*> enrolledStudents; // Using pointers for demonstration
    
public:
    Course(string name, string code, int max) 
        : courseName(name), courseCode(code), maxStudents(max) {
        cout << "Course " << courseName << " created" << endl;
    }
    
    // Enroll a student with capacity check
    bool enrollStudent(Student* student) {
        if (enrolledStudents.size() < maxStudents) {
            enrolledStudents.push_back(student);
            cout << student->getName() << " enrolled in " << courseName << endl;
            return true;
        } else {
            cout << "Course is full! Cannot enroll " << student->getName() << endl;
            return false;
        }
    }
    
    // Display course information
    void displayCourseInfo() const {
        cout << "\n=== Course Information ===" << endl;
        cout << "Course Name: " << courseName << endl;
        cout << "Course Code: " << courseCode << endl;
        cout << "Max Students: " << maxStudents << endl;
        cout << "Currently Enrolled: " << enrolledStudents.size() << endl;
        
        if (!enrolledStudents.empty()) {
            cout << "Enrolled Students:" << endl;
            for (const Student* student : enrolledStudents) {
                cout << "  - " << student->getName() 
                     << " (ID: " << student->getStudentID() << ")" << endl;
            }
        }
        cout << "==========================\n" << endl;
    }
    
    // Getter methods
    string getCourseName() const { return courseName; }
    string getCourseCode() const { return courseCode; }
    int getEnrollmentCount() const { return enrolledStudents.size(); }
};

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== ENCAPSULATION DEMONSTRATION: STUDENT MANAGEMENT SYSTEM ===" << endl << endl;
    
    cout << "Initial total students: " << Student::getTotalStudents() << endl;
    
    // ======================
    // CREATING STUDENT OBJECTS
    // ======================
    cout << "\n1. Creating student objects:" << endl;
    
    Student student1("Alice Johnson", 20, "S1001");
    Student student2("Bob Smith", 22, "S1002");
    Student student3("Charlie Brown", 21, "S1003");
    
    cout << "\nCurrent total students: " << Student::getTotalStudents() << endl;
    
    // ======================
    // ACCESSING DATA THROUGH PUBLIC INTERFACE
    // ======================
    cout << "\n2. Accessing student data through public methods:" << endl;
    
    // Using getters
    cout << "Student 1 Name: " << student1.getName() << endl;
    cout << "Student 1 Age: " << student1.getAge() << endl;
    cout << "Student 1 ID: " << student1.getStudentID() << endl;
    
    // Using setters with validation
    cout << "\n3. Modifying data through setters:" << endl;
    student1.setAge(21);  // Valid age
    student1.setAge(150); // Invalid age - will be rejected
    
    // ======================
    // WORKING WITH GRADES
    // ======================
    cout << "\n4. Adding grades with validation:" << endl;
    
    student1.addGrade(95.5);  // Valid grade
    student1.addGrade(88.0);  // Valid grade
    student1.addGrade(105.0); // Invalid grade - will be rejected
    student1.addGrade(-10.0); // Invalid grade - will be rejected
    
    student2.addGrade(75.0);
    student2.addGrade(82.5);
    student2.addGrade(90.0);
    
    // Display student information
    student1.displayInfo();
    student2.displayInfo();
    student3.displayInfo();
    
    // ======================
    // DEMONSTRATING ENCAPSULATION PROTECTION
    // ======================
    cout << "\n5. Attempting to access private members (commented out):" << endl;
    
    // These lines would cause COMPILATION ERRORS:
    // student1.name = "Hacked Name";        // Error: 'name' is private
    // student1.age = -10;                   // Error: 'age' is private
    // student1.grades.push_back(100.0);     // Error: 'grades' is private
    
    cout << "Private members are protected by encapsulation!" << endl;
    
    // ======================
    // COURSE ENROLLMENT DEMONSTRATION
    // ======================
    cout << "\n6. Course enrollment system:" << endl;
    
    Course mathCourse("Mathematics", "MATH101", 2);
    Course csCourse("Computer Science", "CS101", 3);
    
    // Enroll students
    mathCourse.enrollStudent(&student1);
    mathCourse.enrollStudent(&student2);
    mathCourse.enrollStudent(&student3); // This should fail (course full)
    
    csCourse.enrollStudent(&student1);
    csCourse.enrollStudent(&student2);
    csCourse.enrollStudent(&student3);
    
    // Display course information
    mathCourse.displayCourseInfo();
    csCourse.displayCourseInfo();
    
    // ======================
    // WORKING WITH CONST OBJECTS
    // ======================
    cout << "\n7. Working with const objects:" << endl;
    
    const Student constStudent("David Wilson", 23, "S1004");
    
    // Can only call const member functions
    cout << "Const student name: " << constStudent.getName() << endl;
    cout << "Const student age: " << constStudent.getAge() << endl;
    
    // These would cause compilation errors:
    // constStudent.setAge(24);        // Error: setAge() is not const
    // constStudent.addGrade(85.0);    // Error: addGrade() is not const
    
    constStudent.displayInfo(); // displayInfo() is const, so it's allowed
    
    // ======================
    // SCOPE DEMONSTRATION
    // ======================
    cout << "\n8. Scope demonstration:" << endl;
    {
        cout << "Entering inner scope..." << endl;
        Student tempStudent("Temporary Student", 19, "S9999");
        tempStudent.displayInfo();
        cout << "Exiting inner scope (destructor will be called)..." << endl;
    }
    
    cout << "\nFinal total students: " << Student::getTotalStudents() << endl;
    
    cout << "\n===========================================" << endl << endl;
    cout << "ENCAPSULATION BENEFITS DEMONSTRATED:" << endl;
    cout << "1. Data Protection: Private members are safe from external modification" << endl;
    cout << "2. Validation: Setters can validate data before assignment" << endl;
    cout << "3. Flexibility: Internal implementation can change without affecting users" << endl;
    cout << "4. Controlled Access: Public interface controls how data is accessed" << endl;
    cout << "5. Maintainability: Changes are localized to the class" << endl;
    
    return 0;
}

Encapsulation Benefits

  • Data Hiding: Internal representation is hidden from outside
  • Increased Security: Controlled access through public methods
  • Flexibility & Maintainability: Can change internal implementation without affecting code that uses the class
  • Data Validation: Setters can validate data before storing
  • Modularity: Classes can be developed and tested independently

4. Inheritance: Code Reusability and Hierarchy

Inheritance allows a new class (derived class) to inherit properties and behaviors from an existing class (base class). This promotes code reusability and establishes relationships between classes.

Inheritance Syntax
// BASE CLASS (Parent)
class BaseClass {
protected:
    int protectedData;
public:
    void baseMethod() { }
};

// DERIVED CLASS (Child) - Single Inheritance
class DerivedClass : public BaseClass {
public:
    void derivedMethod() {
        protectedData = 10; // Can access protected members
        baseMethod();       // Can access public methods
    }
};

// MULTIPLE INHERITANCE
class MultiDerived : public Base1, public Base2 {
    // Inherits from both Base1 and Base2
};
Inheritance Examples: Vehicle Hierarchy System
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// ======================
// 1. BASE CLASS: VEHICLE
// ======================
class Vehicle {
protected:
    // Protected members - accessible in derived classes
    string brand;
    string model;
    int year;
    double price;
    
public:
    // Constructor
    Vehicle(string b, string m, int y, double p) 
        : brand(b), model(m), year(y), price(p) {
        cout << "Vehicle constructor: " << brand << " " << model << endl;
    }
    
    // Virtual destructor (IMPORTANT for polymorphism)
    virtual ~Vehicle() {
        cout << "Vehicle destructor: " << brand << " " << model << endl;
    }
    
    // Public methods
    virtual void displayInfo() const {
        cout << "\n=== Vehicle Information ===" << endl;
        cout << "Brand: " << brand << endl;
        cout << "Model: " << model << endl;
        cout << "Year: " << year << endl;
        cout << "Price: $" << price << endl;
    }
    
    // Pure virtual function (makes Vehicle abstract)
    virtual void start() const = 0; // Pure virtual
    
    // Getter methods
    string getBrand() const { return brand; }
    string getModel() const { return model; }
    int getYear() const { return year; }
    double getPrice() const { return price; }
    
    // Setter methods
    void setPrice(double newPrice) {
        if (newPrice > 0) {
            price = newPrice;
        }
    }
};

// ======================
// 2. DERIVED CLASS: CAR (Single Inheritance)
// ======================
class Car : public Vehicle {
private:
    int doors;
    string fuelType;
    
public:
    // Constructor
    Car(string b, string m, int y, double p, int d, string f)
        : Vehicle(b, m, y, p), doors(d), fuelType(f) {
        cout << "Car constructor: " << brand << " " << model << endl;
    }
    
    // Override displayInfo() - polymorphism
    void displayInfo() const override {
        Vehicle::displayInfo(); // Call base class method
        cout << "Doors: " << doors << endl;
        cout << "Fuel Type: " << fuelType << endl;
        cout << "Type: Car" << endl;
    }
    
    // Implement pure virtual function
    void start() const override {
        cout << brand << " " << model << " car starting... Vroom Vroom!" << endl;
    }
    
    // Car-specific method
    void honk() const {
        cout << brand << " " << model << " says: Beep Beep!" << endl;
    }
    
    // Getter methods
    int getDoors() const { return doors; }
    string getFuelType() const { return fuelType; }
};

// ======================
// 3. DERIVED CLASS: MOTORCYCLE
// ======================
class Motorcycle : public Vehicle {
private:
    bool hasSidecar;
    string engineType;
    
public:
    // Constructor
    Motorcycle(string b, string m, int y, double p, bool sidecar, string engine)
        : Vehicle(b, m, y, p), hasSidecar(sidecar), engineType(engine) {
        cout << "Motorcycle constructor: " << brand << " " << model << endl;
    }
    
    // Override displayInfo()
    void displayInfo() const override {
        Vehicle::displayInfo();
        cout << "Has Sidecar: " << (hasSidecar ? "Yes" : "No") << endl;
        cout << "Engine Type: " << engineType << endl;
        cout << "Type: Motorcycle" << endl;
    }
    
    // Implement pure virtual function
    void start() const override {
        cout << brand << " " << model << " motorcycle starting... Vroom!" << endl;
    }
    
    // Motorcycle-specific method
    void wheelie() const {
        cout << brand << " " << model << " is doing a wheelie!" << endl;
    }
};

// ======================
// 4. MULTILEVEL INHERITANCE: ELECTRIC CAR
// ======================
class ElectricCar : public Car {
private:
    double batteryCapacity; // kWh
    int range; // miles
    
public:
    // Constructor
    ElectricCar(string b, string m, int y, double p, int d, 
                double battery, int r)
        : Car(b, m, y, p, d, "Electric"), 
          batteryCapacity(battery), range(r) {
        cout << "ElectricCar constructor: " << brand << " " << model << endl;
    }
    
    // Override displayInfo()
    void displayInfo() const override {
        Car::displayInfo(); // Call parent class method
        cout << "Battery Capacity: " << batteryCapacity << " kWh" << endl;
        cout << "Range: " << range << " miles" << endl;
        cout << "Subtype: Electric Car" << endl;
    }
    
    // Override start() method
    void start() const override {
        cout << brand << " " << model << " electric car starting... Silent Whirr!" << endl;
    }
    
    // ElectricCar-specific method
    void chargeBattery() const {
        cout << brand << " " << model << " is charging..." << endl;
        cout << "Battery: " << batteryCapacity << " kWh" << endl;
        cout << "Range: " << range << " miles" << endl;
    }
};

// ======================
// 5. MULTIPLE INHERITANCE: AMPHIBIOUS VEHICLE
// ======================
class Boat {
protected:
    double length; // in feet
    string hullMaterial;
    
public:
    Boat(double l, string material) : length(l), hullMaterial(material) {}
    
    void floatOnWater() const {
        cout << "Floating on water..." << endl;
    }
    
    void displayBoatInfo() const {
        cout << "Boat Length: " << length << " feet" << endl;
        cout << "Hull Material: " << hullMaterial << endl;
    }
};

class AmphibiousVehicle : public Car, public Boat {
private:
    bool isInWater;
    
public:
    // Constructor - must initialize both parent classes
    AmphibiousVehicle(string b, string m, int y, double p, int d,
                      double boatLength, string material)
        : Car(b, m, y, p, d, "Hybrid"),
          Boat(boatLength, material),
          isInWater(false) {
        cout << "AmphibiousVehicle constructor: " << brand << " " << model << endl;
    }
    
    // Override displayInfo() - need to resolve ambiguity
    void displayInfo() const override {
        cout << "\n=== Amphibious Vehicle Information ===" << endl;
        cout << "Brand: " << Car::getBrand() << endl; // Specify which parent
        cout << "Model: " << Car::getModel() << endl;
        cout << "Year: " << Car::getYear() << endl;
        cout << "Price: $" << Car::getPrice() << endl;
        cout << "Doors: " << getDoors() << endl;
        cout << "Fuel Type: " << getFuelType() << endl;
        Boat::displayBoatInfo();
        cout << "Currently: " << (isInWater ? "In Water" : "On Land") << endl;
    }
    
    // Override start()
    void start() const override {
        if (isInWater) {
            cout << "Starting amphibious vehicle in water mode..." << endl;
            floatOnWater();
        } else {
            cout << "Starting amphibious vehicle in land mode..." << endl;
            Car::honk();
        }
    }
    
    // Switch between land and water
    void enterWater() {
        isInWater = true;
        cout << Car::getBrand() << " " << Car::getModel() << " entered water" << endl;
    }
    
    void exitWater() {
        isInWater = false;
        cout << Car::getBrand() << " " << Car::getModel() << " exited to land" << endl;
    }
};

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== INHERITANCE DEMONSTRATION: VEHICLE HIERARCHY ===" << endl << endl;
    
    // ======================
    // SINGLE INHERITANCE
    // ======================
    cout << "1. Single Inheritance (Car from Vehicle):" << endl;
    
    Car myCar("Toyota", "Camry", 2022, 25000.00, 4, "Gasoline");
    myCar.displayInfo();
    myCar.start();
    myCar.honk();
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // POLYMORPHISM THROUGH BASE POINTER
    // ======================
    cout << "2. Polymorphism using base class pointer:" << endl;
    
    Vehicle* vehiclePtr;
    
    // Point to Car object
    vehiclePtr = &myCar;
    cout << "\nVehicle pointer pointing to Car:" << endl;
    vehiclePtr->displayInfo(); // Calls Car's displayInfo()
    vehiclePtr->start();       // Calls Car's start()
    
    // Point to Motorcycle object
    Motorcycle myBike("Harley", "Davidson", 2021, 15000.00, false, "V-Twin");
    vehiclePtr = &myBike;
    
    cout << "\nVehicle pointer pointing to Motorcycle:" << endl;
    vehiclePtr->displayInfo(); // Calls Motorcycle's displayInfo()
    vehiclePtr->start();       // Calls Motorcycle's start()
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // MULTILEVEL INHERITANCE
    // ======================
    cout << "3. Multilevel Inheritance (ElectricCar from Car):" << endl;
    
    ElectricCar tesla("Tesla", "Model 3", 2023, 45000.00, 4, 75.0, 315);
    tesla.displayInfo();
    tesla.start();
    tesla.honk();        // Inherited from Car
    tesla.chargeBattery(); // ElectricCar specific
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // MULTIPLE INHERITANCE
    // ======================
    cout << "4. Multiple Inheritance (AmphibiousVehicle from Car and Boat):" << endl;
    
    AmphibiousVehicle amphib("WaterCar", "Amphib-X", 2023, 100000.00, 2, 15.5, "Fiberglass");
    amphib.displayInfo();
    
    // Start on land
    amphib.start();
    
    // Enter water and start
    amphib.enterWater();
    amphib.start();
    
    // Exit water
    amphib.exitWater();
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // ARRAY OF BASE POINTERS
    // ======================
    cout << "5. Array of base class pointers (Polymorphism in action):" << endl;
    
    vector<Vehicle*> vehicleFleet;
    
    // Create different types of vehicles
    vehicleFleet.push_back(new Car("Honda", "Civic", 2022, 22000.00, 4, "Gasoline"));
    vehicleFleet.push_back(new Motorcycle("Yamaha", "R1", 2023, 18000.00, false, "Inline-4"));
    vehicleFleet.push_back(new ElectricCar("Ford", "Mustang Mach-E", 2023, 50000.00, 4, 88.0, 305));
    vehicleFleet.push_back(new AmphibiousVehicle("Amphi", "Rover", 2022, 120000.00, 4, 18.0, "Aluminum"));
    
    // Process all vehicles polymorphically
    cout << "\nProcessing vehicle fleet:" << endl;
    for (Vehicle* v : vehicleFleet) {
        cout << "\n-----------------------------" << endl;
        v->displayInfo();
        v->start();
        cout << "-----------------------------" << endl;
    }
    
    // ======================
    // ACCESS CONTROL DEMONSTRATION
    // ======================
    cout << "\n6. Access Control in Inheritance:" << endl;
    
    // Public inheritance: public stays public, protected stays protected
    cout << "Car (publicly inherited from Vehicle):" << endl;
    cout << "Public members accessible: ";
    cout << myCar.getBrand() << " " << myCar.getModel() << endl;
    
    // These would cause compilation errors:
    // myCar.brand = "Hacked";     // Error: brand is protected
    // myCar.year = 2025;          // Error: year is protected
    // myCar.price = 10000;        // Error: price is protected
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // CLEANUP
    // ======================
    cout << "7. Cleanup (calling destructors):" << endl;
    
    // Delete dynamically allocated objects
    for (Vehicle* v : vehicleFleet) {
        delete v; // Virtual destructor ensures proper cleanup
    }
    vehicleFleet.clear();
    
    cout << "\n===========================================" << endl << endl;
    
    cout << "INHERITANCE TYPES SUMMARY:" << endl;
    cout << "1. Single: One base class, one derived class" << endl;
    cout << "2. Multilevel: Derived class becomes base for another" << endl;
    cout << "3. Multiple: Derived from multiple base classes" << endl;
    cout << "4. Hierarchical: Multiple classes derived from one base" << endl;
    cout << "5. Hybrid: Combination of multiple and multilevel" << endl;
    
    cout << "\nINHERITANCE ACCESS SPECIFIERS:" << endl;
    cout << "- public: public→public, protected→protected" << endl;
    cout << "- protected: public→protected, protected→protected" << endl;
    cout << "- private: public→private, protected→private" << endl;
    
    return 0;
}
Types of Inheritance
  • Single: One base, one derived
  • Multiple: Multiple base classes
  • Multilevel: Chain of inheritance
  • Hierarchical: Multiple derived from one base
  • Hybrid: Combination of above
Access Specifiers in Inheritance
  • public: Most common, "is-a" relationship
  • protected: Less common, intermediate access
  • private: Rare, "implemented-in-terms-of"
  • Affects how base members are inherited
Important Rules
  • Always make destructor virtual in base class
  • Use public inheritance for "is-a" relationships
  • Avoid multiple inheritance when possible
  • Use virtual inheritance to solve diamond problem
  • Initialize all base classes in constructor

5. Polymorphism: One Interface, Multiple Forms

Polymorphism allows objects of different classes to be treated as objects of a common base class. It enables one interface to be used for a general class of actions.

Polymorphism Implementation
// BASE CLASS with virtual function
class Base {
public:
    virtual void show() {        // Virtual function
        cout << "Base show()" << endl;
    }
    
    virtual void display() = 0;  // Pure virtual function (abstract)
    
    virtual ~Base() {}           // Virtual destructor
};

// DERIVED CLASS overriding virtual function
class Derived : public Base {
public:
    void show() override {       // Override keyword (C++11)
        cout << "Derived show()" << endl;
    }
    
    void display() override {
        cout << "Derived display()" << endl;
    }
};
Polymorphism Examples: Employee Management System
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;

// ======================
// 1. ABSTRACT BASE CLASS: EMPLOYEE
// ======================
class Employee {
protected:
    string name;
    int id;
    double baseSalary;
    
public:
    // Constructor
    Employee(string n, int i, double salary) 
        : name(n), id(i), baseSalary(salary) {}
    
    // Virtual destructor (CRITICAL for polymorphism)
    virtual ~Employee() {
        cout << "Employee destructor: " << name << endl;
    }
    
    // Pure virtual function - makes Employee abstract
    virtual double calculateSalary() const = 0;
    
    // Virtual function with implementation
    virtual void displayInfo() const {
        cout << "\n=== Employee Information ===" << endl;
        cout << "Name: " << name << endl;
        cout << "ID: " << id << endl;
        cout << "Base Salary: $" << fixed << setprecision(2) << baseSalary << endl;
    }
    
    // Non-virtual function
    string getName() const {
        return name;
    }
    
    int getID() const {
        return id;
    }
};

// ======================
// 2. DERIVED CLASS: FULL-TIME EMPLOYEE
// ======================
class FullTimeEmployee : public Employee {
private:
    double bonus;
    int yearsOfService;
    
public:
    FullTimeEmployee(string n, int i, double salary, double b, int years)
        : Employee(n, i, salary), bonus(b), yearsOfService(years) {}
    
    // Override pure virtual function
    double calculateSalary() const override {
        // Full-time: base + bonus + (years * 1000)
        return baseSalary + bonus + (yearsOfService * 1000.0);
    }
    
    // Override virtual function
    void displayInfo() const override {
        Employee::displayInfo();
        cout << "Type: Full-Time Employee" << endl;
        cout << "Bonus: $" << bonus << endl;
        cout << "Years of Service: " << yearsOfService << endl;
        cout << "Total Salary: $" << calculateSalary() << endl;
    }
    
    // FullTimeEmployee specific method
    void giveRaise(double amount) {
        baseSalary += amount;
        cout << name << " received a raise of $" << amount << endl;
    }
};

// ======================
// 3. DERIVED CLASS: PART-TIME EMPLOYEE
// ======================
class PartTimeEmployee : public Employee {
private:
    int hoursWorked;
    double hourlyRate;
    
public:
    PartTimeEmployee(string n, int i, double rate, int hours)
        : Employee(n, i, 0), hourlyRate(rate), hoursWorked(hours) {}
    
    // Override pure virtual function
    double calculateSalary() const override {
        // Part-time: hours * rate
        return hoursWorked * hourlyRate;
    }
    
    // Override virtual function
    void displayInfo() const override {
        Employee::displayInfo();
        cout << "Type: Part-Time Employee" << endl;
        cout << "Hourly Rate: $" << hourlyRate << endl;
        cout << "Hours Worked: " << hoursWorked << endl;
        cout << "Total Salary: $" << calculateSalary() << endl;
    }
    
    // PartTimeEmployee specific method
    void addHours(int additionalHours) {
        hoursWorked += additionalHours;
        cout << name << " worked " << additionalHours << " more hours" << endl;
    }
};

// ======================
// 4. DERIVED CLASS: MANAGER (with extra functionality)
// ======================
class Manager : public FullTimeEmployee {
private:
    string department;
    int teamSize;
    
public:
    Manager(string n, int i, double salary, double b, int years,
            string dept, int team)
        : FullTimeEmployee(n, i, salary, b, years), 
          department(dept), teamSize(team) {}
    
    // Override calculateSalary()
    double calculateSalary() const override {
        // Manager: FullTime salary + (teamSize * 500)
        return FullTimeEmployee::calculateSalary() + (teamSize * 500.0);
    }
    
    // Override displayInfo()
    void displayInfo() const override {
        FullTimeEmployee::displayInfo();
        cout << "Role: Manager" << endl;
        cout << "Department: " << department << endl;
        cout << "Team Size: " << teamSize << endl;
        cout << "Manager Bonus: $" << (teamSize * 500.0) << endl;
        cout << "Final Salary: $" << calculateSalary() << endl;
    }
    
    // Manager specific method
    void conductMeeting() const {
        cout << name << " is conducting a meeting with " 
             << teamSize << " team members" << endl;
    }
};

// ======================
// 5. DERIVED CLASS: INTERN
// ======================
class Intern : public Employee {
private:
    string university;
    int duration; // in months
    
public:
    Intern(string n, int i, double stipend, string uni, int dur)
        : Employee(n, i, stipend), university(uni), duration(dur) {}
    
    // Override calculateSalary() - interns get fixed stipend
    double calculateSalary() const override {
        return baseSalary; // Fixed stipend
    }
    
    // Override displayInfo()
    void displayInfo() const override {
        Employee::displayInfo();
        cout << "Type: Intern" << endl;
        cout << "University: " << university << endl;
        cout << "Duration: " << duration << " months" << endl;
        cout << "Stipend: $" << calculateSalary() << endl;
    }
    
    // Intern specific method
    void extendInternship(int additionalMonths) {
        duration += additionalMonths;
        cout << name << "'s internship extended by " 
             << additionalMonths << " months" << endl;
    }
};

// ======================
// 6. FUNCTION DEMONSTRATING POLYMORPHISM
// ======================
void processEmployee(Employee* emp) {
    cout << "\nProcessing employee: " << emp->getName() << endl;
    emp->displayInfo();
    cout << "Calculated Salary: $" << emp->calculateSalary() << endl;
}

// Function that works with Employee references
void giveYearEndBonus(Employee& emp) {
    cout << "\nYear-end bonus processing for: " << emp.getName() << endl;
    emp.displayInfo();
    // Each employee type gets bonus differently (polymorphism)
}

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== POLYMORPHISM DEMONSTRATION: EMPLOYEE MANAGEMENT SYSTEM ===" << endl << endl;
    
    cout << fixed << setprecision(2);
    
    // ======================
    // CREATING DIFFERENT TYPES OF EMPLOYEES
    // ======================
    cout << "1. Creating different types of employees:" << endl;
    
    FullTimeEmployee ftEmp("Alice Johnson", 1001, 60000.00, 5000.00, 5);
    PartTimeEmployee ptEmp("Bob Smith", 1002, 25.00, 80);
    Manager mgrEmp("Charlie Brown", 1003, 80000.00, 10000.00, 8, "Engineering", 10);
    Intern intEmp("David Wilson", 1004, 2000.00, "Tech University", 6);
    
    // ======================
    // DIRECT METHOD CALLS
    // ======================
    cout << "\n2. Direct method calls on each object:" << endl;
    
    ftEmp.displayInfo();
    ptEmp.displayInfo();
    mgrEmp.displayInfo();
    intEmp.displayInfo();
    
    // ======================
    // POLYMORPHISM USING BASE CLASS POINTERS
    // ======================
    cout << "\n3. Polymorphism using base class pointers:" << endl;
    
    Employee* empPtr;
    
    // Point to FullTimeEmployee
    empPtr = &ftEmp;
    cout << "\nEmployee pointer pointing to FullTimeEmployee:" << endl;
    empPtr->displayInfo(); // Calls FullTimeEmployee's displayInfo()
    cout << "Salary via pointer: $" << empPtr->calculateSalary() << endl;
    
    // Point to PartTimeEmployee
    empPtr = &ptEmp;
    cout << "\nEmployee pointer pointing to PartTimeEmployee:" << endl;
    empPtr->displayInfo(); // Calls PartTimeEmployee's displayInfo()
    cout << "Salary via pointer: $" << empPtr->calculateSalary() << endl;
    
    // ======================
    // ARRAY OF BASE POINTERS
    // ======================
    cout << "\n4. Array of base class pointers (true polymorphism):" << endl;
    
    vector<Employee*> companyEmployees;
    
    // Add different types of employees to the vector
    companyEmployees.push_back(new FullTimeEmployee("Eve Davis", 1005, 55000.00, 4000.00, 3));
    companyEmployees.push_back(new PartTimeEmployee("Frank Miller", 1006, 20.00, 60));
    companyEmployees.push_back(new Manager("Grace Lee", 1007, 75000.00, 8000.00, 6, "Marketing", 8));
    companyEmployees.push_back(new Intern("Henry Taylor", 1008, 1500.00, "Business College", 3));
    companyEmployees.push_back(new FullTimeEmployee("Ivy Chen", 1009, 65000.00, 6000.00, 7));
    
    // Process all employees polymorphically
    cout << "\nProcessing all employees in company:" << endl;
    cout << "=====================================" << endl;
    
    double totalPayroll = 0.0;
    
    for (Employee* emp : companyEmployees) {
        cout << "\n";
        emp->displayInfo();
        double salary = emp->calculateSalary();
        totalPayroll += salary;
        cout << "Monthly Salary: $" << salary << endl;
        cout << "-------------------------------------" << endl;
    }
    
    cout << "\nTotal Monthly Payroll: $" << totalPayroll << endl;
    cout << "=====================================" << endl;
    
    // ======================
    // FUNCTION DEMONSTRATING POLYMORPHISM
    // ======================
    cout << "\n5. Functions working with base class pointers/references:" << endl;
    
    processEmployee(&ftEmp);
    processEmployee(&mgrEmp);
    
    // Using references
    giveYearEndBonus(ptEmp);
    giveYearEndBonus(intEmp);
    
    // ======================
    // TYPEID AND DYNAMIC_CAST
    // ======================
    cout << "\n6. Runtime Type Identification (RTTI):" << endl;
    
    for (Employee* emp : companyEmployees) {
        cout << "\nEmployee: " << emp->getName() << endl;
        
        // Using typeid
        cout << "Type: " << typeid(*emp).name() << endl;
        
        // Using dynamic_cast to check specific types
        if (FullTimeEmployee* ft = dynamic_cast<FullTimeEmployee*>(emp)) {
            cout << "This is a FullTimeEmployee (or derived from it)" << endl;
            // Can call FullTimeEmployee specific methods
        }
        
        if (Manager* mgr = dynamic_cast<Manager*>(emp)) {
            cout << "This is a Manager!" << endl;
            mgr->conductMeeting();
        }
        
        if (Intern* intern = dynamic_cast<Intern*>(emp)) {
            cout << "This is an Intern from " << intern->getName() << endl;
        }
    }
    
    // ======================
    // VIRTUAL DESTRUCTOR DEMONSTRATION
    // ======================
    cout << "\n7. Virtual destructor demonstration:" << endl;
    
    Employee* testEmp = new Manager("Test Manager", 9999, 90000.00, 10000.00, 
                                    10, "Testing", 5);
    
    cout << "\nCreated Manager object via Employee pointer" << endl;
    testEmp->displayInfo();
    
    cout << "\nDeleting via Employee pointer..." << endl;
    delete testEmp; // Virtual destructor ensures Manager destructor is called
    
    // ======================
    // ABSTRACT CLASS DEMONSTRATION
    // ======================
    cout << "\n8. Abstract class demonstration:" << endl;
    
    // This would cause compilation error:
    // Employee abstractEmp("Abstract", 0000, 0); // Error: cannot instantiate abstract class
    
    cout << "Employee is an abstract class (has pure virtual function)" << endl;
    cout << "You can only create objects of concrete derived classes" << endl;
    
    // ======================
    // CLEANUP
    // ======================
    cout << "\n9. Cleaning up dynamically allocated memory:" << endl;
    
    for (Employee* emp : companyEmployees) {
        delete emp; // Virtual destructor ensures proper cleanup
    }
    companyEmployees.clear();
    
    cout << "\n===========================================" << endl << endl;
    
    cout << "POLYMORPHISM KEY CONCEPTS:" << endl;
    cout << "1. Compile-time (Static) Polymorphism:" << endl;
    cout << "   - Function overloading" << endl;
    cout << "   - Operator overloading" << endl;
    cout << "   - Templates" << endl;
    cout << endl;
    cout << "2. Runtime (Dynamic) Polymorphism:" << endl;
    cout << "   - Virtual functions" << endl;
    cout << "   - Function overriding" << endl;
    cout << "   - Achieved through inheritance and pointers/references" << endl;
    cout << endl;
    cout << "3. Key Requirements:" << endl;
    cout << "   - Inheritance hierarchy" << endl;
    cout << "   - Virtual functions in base class" << endl;
    cout << "   - Base class pointers/references to derived objects" << endl;
    cout << "   - Virtual destructor in base class" << endl;
    
    return 0;
}

Types of Polymorphism in C++

Compile-Time Polymorphism (Static)
  • Function Overloading: Same name, different parameters
  • Operator Overloading: Custom behavior for operators
  • Templates: Generic programming
  • Resolved at compile time
  • Faster execution
Runtime Polymorphism (Dynamic)
  • Virtual Functions: Function overriding
  • Function Overriding: Derived class redefines base function
  • Achieved through inheritance
  • Resolved at runtime
  • Flexible but slightly slower
Virtual Function Rules:
  • Always declare destructor as virtual in base class
  • Use override keyword (C++11) to explicitly indicate overriding
  • Use final keyword (C++11) to prevent further overriding
  • Pure virtual functions make a class abstract (cannot instantiate)
  • Virtual functions have small performance overhead (vtable lookup)
  • Constructors cannot be virtual, destructors should be virtual

6. Abstraction: Hiding Complexity

Abstraction is the process of hiding complex implementation details and showing only essential features to the user. In C++, abstraction is achieved through abstract classes and interfaces.

Abstraction Implementation
// ABSTRACT CLASS (contains pure virtual function)
class AbstractClass {
public:
    // Pure virtual function - makes class abstract
    virtual void essentialOperation() = 0;
    
    // Virtual destructor
    virtual ~AbstractClass() {}
    
    // Can have implemented methods too
    void commonOperation() {
        cout << "Common implementation" << endl;
    }
};

// CONCRETE CLASS implementing abstraction
class ConcreteClass : public AbstractClass {
public:
    void essentialOperation() override {
        cout << "Concrete implementation" << endl;
    }
};
Abstraction Examples: Device Control System
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

// ======================
// 1. ABSTRACT INTERFACE: ELECTRONIC DEVICE
// ======================
class ElectronicDevice {
public:
    // Pure virtual functions - interface contract
    virtual void turnOn() = 0;
    virtual void turnOff() = 0;
    virtual void adjustSettings() = 0;
    virtual string getStatus() const = 0;
    
    // Virtual destructor
    virtual ~ElectronicDevice() {
        cout << "ElectronicDevice destroyed" << endl;
    }
    
    // Common non-virtual function
    void displayDeviceInfo() const {
        cout << "\nDevice Information:" << endl;
        cout << "Status: " << getStatus() << endl;
    }
};

// ======================
// 2. CONCRETE CLASS: TELEVISION
// ======================
class Television : public ElectronicDevice {
private:
    string brand;
    int channel;
    int volume;
    bool isOn;
    
public:
    Television(string b) : brand(b), channel(1), volume(50), isOn(false) {}
    
    // Implement interface methods
    void turnOn() override {
        isOn = true;
        cout << brand << " TV is now ON" << endl;
        cout << "Channel: " << channel << ", Volume: " << volume << endl;
    }
    
    void turnOff() override {
        isOn = false;
        cout << brand << " TV is now OFF" << endl;
    }
    
    void adjustSettings() override {
        if (isOn) {
            cout << "Adjusting " << brand << " TV settings..." << endl;
            channel = (channel % 100) + 1;
            volume = (volume + 10) % 100;
            cout << "Changed to Channel: " << channel 
                 << ", Volume: " << volume << endl;
        } else {
            cout << "Cannot adjust - TV is OFF" << endl;
        }
    }
    
    string getStatus() const override {
        return isOn ? "ON (Channel: " + to_string(channel) + 
                      ", Volume: " + to_string(volume) + "%)" : "OFF";
    }
    
    // Television-specific methods
    void changeChannel(int newChannel) {
        if (isOn && newChannel > 0 && newChannel <= 999) {
            channel = newChannel;
            cout << "Changed to channel " << channel << endl;
        }
    }
};

// ======================
// 3. CONCRETE CLASS: AIR CONDITIONER
// ======================
class AirConditioner : public ElectronicDevice {
private:
    string model;
    int temperature;
    string mode; // Cool, Heat, Fan, Dry
    bool isOn;
    
public:
    AirConditioner(string m, int temp = 24) 
        : model(m), temperature(temp), mode("Cool"), isOn(false) {}
    
    // Implement interface methods
    void turnOn() override {
        isOn = true;
        cout << model << " AC is now ON" << endl;
        cout << "Mode: " << mode << ", Temperature: " << temperature << "°C" << endl;
    }
    
    void turnOff() override {
        isOn = false;
        cout << model << " AC is now OFF" << endl;
    }
    
    void adjustSettings() override {
        if (isOn) {
            cout << "Adjusting " << model << " AC settings..." << endl;
            
            // Cycle through modes
            if (mode == "Cool") mode = "Heat";
            else if (mode == "Heat") mode = "Fan";
            else if (mode == "Fan") mode = "Dry";
            else mode = "Cool";
            
            // Adjust temperature
            temperature = (temperature % 30) + 16; // Between 16-30°C
            
            cout << "Mode: " << mode << ", Temperature: " << temperature << "°C" << endl;
        } else {
            cout << "Cannot adjust - AC is OFF" << endl;
        }
    }
    
    string getStatus() const override {
        return isOn ? "ON (Mode: " + mode + 
                      ", Temp: " + to_string(temperature) + "°C)" : "OFF";
    }
    
    // AC-specific method
    void setTemperature(int temp) {
        if (isOn && temp >= 16 && temp <= 30) {
            temperature = temp;
            cout << "Temperature set to " << temperature << "°C" << endl;
        }
    }
};

// ======================
// 4. CONCRETE CLASS: SMART LIGHT
// ======================
class SmartLight : public ElectronicDevice {
private:
    string location;
    int brightness; // 0-100%
    string color;
    bool isOn;
    
public:
    SmartLight(string loc, string col = "White") 
        : location(loc), brightness(50), color(col), isOn(false) {}
    
    // Implement interface methods
    void turnOn() override {
        isOn = true;
        cout << location << " light is now ON" << endl;
        cout << "Brightness: " << brightness << "%, Color: " << color << endl;
    }
    
    void turnOff() override {
        isOn = false;
        cout << location << " light is now OFF" << endl;
    }
    
    void adjustSettings() override {
        if (isOn) {
            cout << "Adjusting " << location << " light settings..." << endl;
            
            // Cycle through colors
            if (color == "White") color = "Warm White";
            else if (color == "Warm White") color = "Blue";
            else if (color == "Blue") color = "Red";
            else if (color == "Red") color = "Green";
            else color = "White";
            
            // Adjust brightness
            brightness = (brightness + 25) % 100;
            if (brightness == 0) brightness = 25;
            
            cout << "Color: " << color << ", Brightness: " << brightness << "%" << endl;
        } else {
            cout << "Cannot adjust - Light is OFF" << endl;
        }
    }
    
    string getStatus() const override {
        return isOn ? "ON (Color: " + color + 
                      ", Brightness: " + to_string(brightness) + "%)" : "OFF";
    }
    
    // Light-specific method
    void setBrightness(int level) {
        if (isOn && level >= 0 && level <= 100) {
            brightness = level;
            cout << "Brightness set to " << brightness << "%" << endl;
        }
    }
};

// ======================
// 5. HOME AUTOMATION SYSTEM (Using Abstraction)
// ======================
class HomeAutomationSystem {
private:
    vector<ElectronicDevice*> devices;
    
public:
    void addDevice(ElectronicDevice* device) {
        devices.push_back(device);
        cout << "Device added to home automation system" << endl;
    }
    
    void turnOnAllDevices() {
        cout << "\n=== Turning ON All Devices ===" << endl;
        for (ElectronicDevice* device : devices) {
            device->turnOn();
        }
    }
    
    void turnOffAllDevices() {
        cout << "\n=== Turning OFF All Devices ===" << endl;
        for (ElectronicDevice* device : devices) {
            device->turnOff();
        }
    }
    
    void adjustAllSettings() {
        cout << "\n=== Adjusting All Device Settings ===" << endl;
        for (ElectronicDevice* device : devices) {
            device->adjustSettings();
        }
    }
    
    void displayAllStatus() {
        cout << "\n=== All Device Status ===" << endl;
        for (ElectronicDevice* device : devices) {
            device->displayDeviceInfo();
        }
    }
    
    // Cleanup
    ~HomeAutomationSystem() {
        for (ElectronicDevice* device : devices) {
            delete device;
        }
        devices.clear();
    }
};

// ======================
// 6. FACTORY PATTERN USING ABSTRACTION
// ======================
class DeviceFactory {
public:
    static ElectronicDevice* createDevice(string type, string name) {
        if (type == "TV") {
            return new Television(name);
        } else if (type == "AC") {
            return new AirConditioner(name);
        } else if (type == "Light") {
            return new SmartLight(name);
        }
        return nullptr;
    }
};

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== ABSTRACTION DEMONSTRATION: HOME AUTOMATION SYSTEM ===" << endl << endl;
    
    // ======================
    // DIRECT USAGE OF CONCRETE CLASSES
    // ======================
    cout << "1. Direct usage of concrete classes:" << endl;
    
    Television livingRoomTV("Samsung");
    AirConditioner bedroomAC("LG");
    SmartLight kitchenLight("Kitchen");
    
    livingRoomTV.turnOn();
    livingRoomTV.adjustSettings();
    livingRoomTV.displayDeviceInfo();
    
    bedroomAC.turnOn();
    bedroomAC.adjustSettings();
    bedroomAC.displayDeviceInfo();
    
    kitchenLight.turnOn();
    kitchenLight.adjustSettings();
    kitchenLight.displayDeviceInfo();
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // ABSTRACTION THROUGH BASE POINTERS
    // ======================
    cout << "2. Abstraction using base class pointers:" << endl;
    
    ElectronicDevice* devicePtr;
    
    // Point to Television
    devicePtr = &livingRoomTV;
    cout << "\nDevice pointer (pointing to TV):" << endl;
    devicePtr->turnOn();
    devicePtr->adjustSettings();
    cout << "Status: " << devicePtr->getStatus() << endl;
    
    // Point to AirConditioner
    devicePtr = &bedroomAC;
    cout << "\nDevice pointer (pointing to AC):" << endl;
    devicePtr->turnOn();
    devicePtr->adjustSettings();
    cout << "Status: " << devicePtr->getStatus() << endl;
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // HOME AUTOMATION SYSTEM
    // ======================
    cout << "3. Home Automation System (Real abstraction in action):" << endl;
    
    HomeAutomationSystem smartHome;
    
    // Create devices using factory
    smartHome.addDevice(DeviceFactory::createDevice("TV", "Sony Bravia"));
    smartHome.addDevice(DeviceFactory::createDevice("AC", "Daikin Inverter"));
    smartHome.addDevice(DeviceFactory::createDevice("Light", "Living Room"));
    smartHome.addDevice(DeviceFactory::createDevice("Light", "Bedroom"));
    
    // Control all devices through abstract interface
    smartHome.turnOnAllDevices();
    smartHome.displayAllStatus();
    
    smartHome.adjustAllSettings();
    smartHome.displayAllStatus();
    
    smartHome.turnOffAllDevices();
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // SMART POINTERS WITH ABSTRACTION
    // ======================
    cout << "4. Smart pointers with abstraction (Modern C++):" << endl;
    
    vector<unique_ptr<ElectronicDevice>> smartDevices;
    
    smartDevices.push_back(make_unique<Television>("Panasonic"));
    smartDevices.push_back(make_unique<AirConditioner>("Hitachi", 22));
    smartDevices.push_back(make_unique<SmartLight>("Bathroom", "Blue"));
    
    for (auto& device : smartDevices) {
        device->turnOn();
        device->adjustSettings();
        device->displayDeviceInfo();
        device->turnOff();
    }
    
    // No need to manually delete - unique_ptr handles it!
    
    cout << "\n===========================================" << endl << endl;
    
    // ======================
    // ABSTRACT CLASS DEMONSTRATION
    // ======================
    cout << "5. Abstract class demonstration:" << endl;
    
    // This would cause compilation error:
    // ElectronicDevice abstractDevice; // Error: cannot instantiate abstract class
    
    cout << "ElectronicDevice is abstract (has pure virtual functions)" << endl;
    cout << "We can only create pointers/references to it" << endl;
    cout << "Concrete classes must implement all pure virtual functions" << endl;
    
    cout << "\n===========================================" << endl << endl;
    
    cout << "ABSTRACTION BENEFITS DEMONSTRATED:" << endl;
    cout << "1. Hides Complexity: Users don't need to know internal implementation" << endl;
    cout << "2. Provides Interface: Consistent interface for different devices" << endl;
    cout << "3. Loose Coupling: System components are independent" << endl;
    cout << "4. Extensibility: Easy to add new device types" << endl;
    cout << "5. Maintainability: Changes to one device don't affect others" << endl;
    
    cout << "\nABSTRACTION vs ENCAPSULATION:" << endl;
    cout << "- Encapsulation: Hides data and implementation details" << endl;
    cout << "- Abstraction: Hides complexity and shows only essentials" << endl;
    cout << "- Encapsulation is about HOW, Abstraction is about WHAT" << endl;
    cout << "- Encapsulation protects data, Abstraction simplifies interface" << endl;
    
    return 0;
}
Abstraction Implementation
  • Abstract classes (pure virtual functions)
  • Interfaces (all pure virtual functions)
  • Header files (.h) as abstraction
  • Libraries and APIs
  • Design Patterns (Factory, Strategy, etc.)
Abstraction vs Encapsulation
  • Abstraction: Design level, what to show
  • Encapsulation: Implementation level, how to hide
  • Abstraction solves design problems
  • Encapsulation solves implementation problems
  • Both work together for good OOP design
When to Use Abstraction
  • Creating libraries/frameworks
  • Defining contracts/interfaces
  • Hiding platform-specific code
  • Creating plug-in architectures
  • When implementation may change

7. OOP Best Practices and Design Principles

OOP Design Principles (SOLID)
  • S - Single Responsibility: One class, one reason to change
  • O - Open/Closed: Open for extension, closed for modification
  • L - Liskov Substitution: Derived classes should substitute base classes
  • I - Interface Segregation: Many specific interfaces better than one general
  • D - Dependency Inversion: Depend on abstractions, not concretions
Common OOP Mistakes
  • God classes (too many responsibilities)
  • Deep inheritance hierarchies
  • Not making destructor virtual in base class
  • Using public data members
  • Circular dependencies between classes
  • Overusing inheritance when composition is better
  • Not following Rule of Three/Five
Good OOP Design vs Bad OOP Design
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

// ======================
// BAD OOP PRACTICES
// ======================

// BAD: God class - does everything
class BadEmployeeSystem {
private:
    // Mixed responsibilities
    vector<string> employeeNames;
    vector<double> salaries;
    vector<string> departments;
    vector<string> projects;
    
    // Also handles payroll, reporting, etc.
public:
    // Too many unrelated methods
    void addEmployee(string name, double salary, string dept) { }
    void calculatePayroll() { }
    void generateReport() { }
    void assignProject(string employee, string project) { }
    void scheduleMeeting() { } // Not related to employee management!
    // ... 50 more methods
};

// BAD: Deep inheritance hierarchy
class BadShape {
    // Base class
};

class Bad2DShape : public BadShape {
    // Level 1
};

class BadPolygon : public Bad2DShape {
    // Level 2
};

class BadQuadrilateral : public BadPolygon {
    // Level 3
};

class BadRectangle : public BadQuadrilateral {
    // Level 4 - too deep!
};

// BAD: Public data members (breaks encapsulation)
class BadBankAccount {
public:
    double balance; // BAD: Should be private!
    string accountNumber;
    // No validation, no protection
};

// ======================
// GOOD OOP PRACTICES
// ======================

// GOOD: Single Responsibility Principle
class Employee {
private:
    string name;
    int id;
    double baseSalary;
public:
    Employee(string n, int i, double s) : name(n), id(i), baseSalary(s) {}
    string getName() const { return name; }
    // Only employee-related methods
};

class PayrollCalculator {
public:
    double calculatePay(const Employee& emp) {
        // Single responsibility: calculate pay
        return 0; // Simplified
    }
};

class ReportGenerator {
public:
    void generateEmployeeReport(const Employee& emp) {
        // Single responsibility: generate reports
    }
};

// GOOD: Composition over Inheritance
class Engine {
public:
    void start() { cout << "Engine started" << endl; }
};

class Wheels {
public:
    void rotate() { cout << "Wheels rotating" << endl; }
};

class Car {
private:
    Engine engine;      // Composition
    Wheels wheels[4];   // Composition
    string model;
public:
    void startCar() {
        engine.start();
        for (auto& wheel : wheels) {
            wheel.rotate();
        }
        cout << model << " is ready to go!" << endl;
    }
};

// GOOD: Interface Segregation Principle
class IPrintable {
public:
    virtual void print() const = 0;
    virtual ~IPrintable() = default;
};

class IScannable {
public:
    virtual void scan() = 0;
    virtual ~IScannable() = default;
};

class IFaxable {
public:
    virtual void fax() = 0;
    virtual ~IFaxable() = default;
};

// Implement only needed interfaces
class BasicPrinter : public IPrintable {
public:
    void print() const override {
        cout << "Printing document..." << endl;
    }
};

class AllInOnePrinter : public IPrintable, public IScannable, public IFaxable {
public:
    void print() const override {
        cout << "Printing document..." << endl;
    }
    
    void scan() override {
        cout << "Scanning document..." << endl;
    }
    
    void fax() override {
        cout << "Sending fax..." << endl;
    }
};

// GOOD: Dependency Inversion Principle
class IMessageSender {
public:
    virtual void sendMessage(string message) = 0;
    virtual ~IMessageSender() = default;
};

class EmailSender : public IMessageSender {
public:
    void sendMessage(string message) override {
        cout << "Sending email: " << message << endl;
    }
};

class SMSSender : public IMessageSender {
public:
    void sendMessage(string message) override {
        cout << "Sending SMS: " << message << endl;
    }
};

class NotificationService {
private:
    IMessageSender& sender; // Depends on abstraction
public:
    NotificationService(IMessageSender& s) : sender(s) {}
    
    void notify(string message) {
        sender.sendMessage(message);
    }
};

// GOOD: Liskov Substitution Principle
class Bird {
public:
    virtual void fly() {
        cout << "Flying..." << endl;
    }
    virtual ~Bird() = default;
};

class Sparrow : public Bird {
public:
    void fly() override {
        cout << "Sparrow flying gracefully..." << endl;
    }
};

// Penguin can't fly, so shouldn't inherit from Bird with fly()
class Penguin {
public:
    void swim() {
        cout << "Penguin swimming..." << endl;
    }
};

// ======================
// MAIN FUNCTION
// ======================
int main() {
    cout << "=== GOOD vs BAD OOP DESIGN ===" << endl << endl;
    
    cout << "1. Single Responsibility Principle:" << endl;
    Employee emp("John", 101, 50000.00);
    PayrollCalculator payroll;
    ReportGenerator reports;
    
    cout << "Employee: " << emp.getName() << endl;
    cout << "Each class has one responsibility" << endl;
    
    cout << "\n2. Composition over Inheritance:" << endl;
    Car myCar;
    myCar.startCar();
    
    cout << "\n3. Interface Segregation:" << endl;
    BasicPrinter basic;
    basic.print();
    
    AllInOnePrinter allInOne;
    allInOne.print();
    allInOne.scan();
    allInOne.fax();
    
    cout << "\n4. Dependency Inversion:" << endl;
    EmailSender emailSender;
    NotificationService emailService(emailSender);
    emailService.notify("Hello via Email");
    
    SMSSender smsSender;
    NotificationService smsService(smsSender);
    smsService.notify("Hello via SMS");
    
    cout << "\n5. Liskov Substitution:" << endl;
    Sparrow sparrow;
    sparrow.fly();
    
    Penguin penguin;
    penguin.swim();
    // penguin.fly(); // Wouldn't compile - good design!
    
    cout << "\n===========================================" << endl << endl;
    
    cout << "OOP BEST PRACTICES SUMMARY:" << endl;
    cout << "1. Favor Composition over Inheritance" << endl;
    cout << "2. Follow SOLID principles" << endl;
    cout << "3. Keep classes small and focused" << endl;
    cout << "4. Use const correctness" << endl;
    cout << "5. Follow Rule of Three/Five/Zero" << endl;
    cout << "6. Use smart pointers for resource management" << endl;
    cout << "7. Make base class destructor virtual" << endl;
    cout << "8. Use override and final keywords (C++11+)" << endl;
    cout << "9. Prefer initialization lists in constructors" << endl;
    cout << "10. Design for testability" << endl;
    
    return 0;
}

Golden Rules of C++ OOP

Design Principles
  • Encapsulate what varies
  • Program to interfaces
  • Favor composition
  • Keep it simple
C++ Specific
  • Virtual destructor in base class
  • Rule of Three/Five/Zero
  • Const correctness
  • RAII (Resource Acquisition Is Initialization)
Modern C++ (C++11+)
  • Use override and final
  • Prefer smart pointers
  • Use move semantics
  • Use auto for type deduction