C++ Staff Management: Inheritance and Polymorphism

Classified in Computers

Written on in English with a size of 10.44 KB

C++ Staff Management System: Inheritance and Polymorphism

This document presents a refactored C++ program demonstrating object-oriented principles such as inheritance and polymorphism. The system models a basic staff management application, featuring a base Officer class and derived classes for specific roles like JusticeOfficer and Firefighter. It highlights dynamic memory management, virtual functions, and runtime polymorphism.

Core Concepts Illustrated

  • Inheritance: JusticeOfficer and Firefighter inherit from the base Officer class.
  • Polymorphism: Virtual functions (insert(), show(), destructor) allow for different behaviors based on the actual object type at runtime.
  • Dynamic Memory: Usage of new and delete[] for character arrays (DNI, names) and object instances.
  • Constructors & Destructors: Proper initialization and cleanup of resources.

Corrected C++ Code Implementation

The original code contained several syntax, naming, and logical errors. The following sections present the corrected and improved C++ code, organized for clarity and adherence to modern C++ practices.

1. Header Files and Namespace

Standard C++ headers are used, and std:: prefix is explicitly added for cout, cin, and endl to avoid polluting the global namespace.

#include <iostream>
#include <cstring> // For strlen and strcpy

// No using namespace std; to avoid potential conflicts

2. Base Class: Officer

The Officer class serves as the base for all staff members. It includes common attributes like DNI (ID) and salary, along with virtual methods for inserting and displaying data, and a virtual destructor for proper cleanup in polymorphic scenarios.

class Officer {
protected:
    char *dni; // Document Number Identification
    double salary;

public:
    // Constructor with default values
    Officer(const char *pdni = "", double salary_val = 0);

    // Virtual destructor for proper memory cleanup in derived classes
    virtual ~Officer();

    // Virtual methods for data insertion and display
    virtual void insert();
    virtual void show();
};

// Officer Class Method Definitions

Officer::Officer(const char *pdni, double salary_val) {
    dni = new char[strlen(pdni) + 1];
    strcpy(dni, pdni);
    salary = salary_val;
    std::cout << "Officer Constructor" << std::endl;
}

Officer::~Officer() {
    delete[] dni;
    std::cout << "Officer Destructor" << std::endl;
}

void Officer::insert() {
    // Prevent memory leak if insert is called multiple times
    if (dni) {
        delete[] dni;
    }
    dni = new char[10]; // Assuming DNI is max 9 chars + null terminator
    std::cout << "\nEnter Officer DNI: ";
    std::cin >> dni;
    std::cout << "Enter Officer Salary: ";
    std::cin >> salary;
}

void Officer::show() {
    std::cout << "\nOfficer with DNI: " << dni << std::endl;
    std::cout << "and salary: " << salary << " Euros." << std::endl;
}

3. Derived Class: JusticeOfficer

The JusticeOfficer class inherits from Officer and adds specific attributes like monthly working hours and the name of the court. It overrides the insert() and show() methods to handle its unique data.

class JusticeOfficer : public Officer {
protected:
    int monthlyHours;
    char *courtName; // Name of the court

public:
    // Constructor with default values, calling base class constructor
    JusticeOfficer(int monthlyHours_val = 0, const char *courtName_val = "", const char *dni_val = "", double salary_val = 0);

    // Destructor for JusticeOfficer specific cleanup
    ~JusticeOfficer();

    // Overridden methods for data insertion and display
    void insert() override;
    void show() override;
};

// JusticeOfficer Class Method Definitions

JusticeOfficer::JusticeOfficer(int monthlyHours_val, const char *courtName_val, const char *dni_val, double salary_val)
    : Officer(dni_val, salary_val) {
    courtName = new char[strlen(courtName_val) + 1];
    strcpy(courtName, courtName_val);
    monthlyHours = monthlyHours_val;
    std::cout << "JusticeOfficer Constructor" << std::endl;
}

JusticeOfficer::~JusticeOfficer() {
    delete[] courtName;
    std::cout << "JusticeOfficer Destructor" << std::endl;
}

void JusticeOfficer::insert() {
    // Call base class insert to get DNI and salary
    Officer::insert(); 

    // Prevent memory leak if insert is called multiple times
    if (courtName) {
        delete[] courtName;
    }
    courtName = new char[30]; // Assuming court name is max 29 chars + null terminator

    std::cout << "Enter number of hours worked per month: ";
    std::cin >> monthlyHours;
    std::cout << "Enter the name of the city court: ";
    std::cin >> courtName;
}

void JusticeOfficer::show() {
    Officer::show(); // Call base class show for common data
    std::cout << "Working " << monthlyHours << " hours per month" << std::endl;
    std::cout << "in the courts of " << courtName << "." << std::endl;
}

4. Derived Class: Firefighter

The Firefighter class also inherits from Officer, adding attributes for the assigned district and promotion year. It provides its own implementations for insert() and show().

class Firefighter : public Officer {
protected:
    char *districtName;
    int promotionYear;

public:
    // Constructor with default values, calling base class constructor
    Firefighter(const char *districtName_val = "", int promotionYear_val = 0, const char *dni_val = "", double salary_val = 0);

    // Destructor for Firefighter specific cleanup
    ~Firefighter();

    // Overridden methods for data insertion and display
    void insert() override;
    void show() override;
};

// Firefighter Class Method Definitions

Firefighter::Firefighter(const char *districtName_val, int promotionYear_val, const char *dni_val, double salary_val)
    : Officer(dni_val, salary_val) {
    districtName = new char[strlen(districtName_val) + 1];
    strcpy(districtName, districtName_val);
    promotionYear = promotionYear_val;
    std::cout << "Firefighter Constructor" << std::endl;
}

Firefighter::~Firefighter() {
    delete[] districtName;
    std::cout << "Firefighter Destructor" << std::endl;
}

void Firefighter::insert() {
    // Call base class insert to get DNI and salary
    Officer::insert();

    // Prevent memory leak if insert is called multiple times
    if (districtName) {
        delete[] districtName;
    }
    districtName = new char[30]; // Assuming district name is max 29 chars + null terminator

    std::cout << "Enter promotion year: ";
    std::cin >> promotionYear;
    std::cout << "Enter assigned district: ";
    std::cin >> districtName;
}

void Firefighter::show() {
    Officer::show(); // Call base class show for common data
    std::cout << "Promotion year: " << promotionYear << std::endl;
    std::cout << "Working in district: " << districtName << "." << std::endl;
}

5. Main Function: Demonstrating Polymorphism

The main function demonstrates the use of polymorphism by creating objects of derived classes and manipulating them through base class pointers. This allows for uniform handling of different staff types.

int main(int argc, char *argv[]) {
    // Create objects using base class pointers
    Officer *fp1 = new Officer("11258789C", 20000);
    Officer *fp2 = new JusticeOfficer(160, "Barcelona", "70546987Z", 19000);
    Officer *fp3 = new Firefighter("Salamanca", 2002, "21546987B", 30000);

    // Create objects with default constructors, to be filled via insert()
    Officer *fp4 = new Firefighter();
    Officer *fp5 = new JusticeOfficer();
    Officer *fp6 = new Officer();

    // Display initial data
    std::cout << "\n--- Initial Data Display ---" << std::endl;
    fp1->show();
    fp2->show();
    fp3->show();

    // Insert data for default-constructed objects and then display
    std::cout << "\n--- Data Insertion and Display for New Objects ---" << std::endl;
    std::cout << "\n--- Inserting data for Firefighter (fp4) ---" << std::endl;
    fp4->insert();
    fp4->show();

    std::cout << "\n--- Inserting data for JusticeOfficer (fp5) ---" << std::endl;
    fp5->insert();
    fp5->show();

    std::cout << "\n--- Inserting data for Officer (fp6) ---" << std::endl;
    fp6->insert();
    fp6->show();

    // Clean up dynamically allocated memory
    delete fp1;
    delete fp2;
    delete fp3;
    delete fp4;
    delete fp5;
    delete fp6;

    return 0;
}

Key Improvements and Best Practices

  • Consistent Naming: Variable names like dni, salary, monthlyHours, courtName, and promotionYear are used consistently.
  • Memory Safety: Added delete[] before re-allocating memory in insert() methods to prevent leaks if called multiple times on the same object.
  • const char* for String Inputs: Ensures that string literals passed to constructors are treated as constant.
  • override Keyword: Explicitly marks overridden virtual functions, improving code readability and compile-time error checking.
  • Base Class insert()/show() Calls: Derived classes now call their base class counterparts to reuse common logic.
  • Standard C++ Headers: Replaced old .h headers with modern equivalents.
  • Standard main Signature: Corrected main function signature to int main(int argc, char *argv[]) and added return 0;.
  • Clearer Output: Improved console output messages for better user experience.

Related entries: