Chintan
Chintan

Reputation: 25

Create a vector of base class objects and store derived class objects within

I am trying to create an employee database (Vector of Employees). There are 3 types of employees ie. employees is the base class and Manager, Engg and Scientist are derived class. Every employee has first name and last name. In addition to the name, each of the 3 types of employees have unique stats ie. Manager has number of meetings/week whereas the Engg has work experience and so on.

I have a couple of questions 1. Should I upcast the derived objects to the base class or downcast the base class to the derived class? 2. How do I use polymorphism to override methods, since I want the user to add an employee type and based on the type selected the respective entry fields should appear ie. in case of a Manager, in addition to the first and last names, the program should also ask for meetings/week?

Here is my Class file

class Employee{
public:
Employee();
 Employee(string fName, string lName, int sal);
virtual void printEmp();
string getFirstName();
string getLastName();

protected:
string m_fName;
string m_lName;
int m_sal;
};


class Manager : public Employee{
public:
Manager();
Manager(string fName, string lName, int sal, int meets, int hols);
void printEmp();


protected:
int m_meets;
int m_hols;
};

Here is the implementation

Employee::Employee(){
m_fName = "Default";
m_lName = "Default";
m_sal = 0;
}


Employee::Employee(string fName, string lName, int sal){
m_fName = fName;
m_lName = lName;
m_sal = sal;
}

void Employee::printEmp(){
cout << "First Name: " << m_fName << endl
    << "Last Name: " << m_lName << endl
    << "Salary: " << m_sal << endl;
}


string Employee::getLastName(){
return m_lName;
}

string Employee::getFirstName(){
return m_fName;
}

Manager::Manager(string fName, string lName, int sal, int meets, int hols) :         Employee(fName, lName, sal), m_meets(meets), m_hols(hols)
{
//empty
}

void Manager::printEmp(){
Employee::printEmp();
cout << "Meets/Week: " << m_meets << endl
    << "Holidays/Year: " << m_hols << endl << endl;

Here is the main

int main(){

    bool exit = false;
    vector<Employee*> dBVector;

    while (!exit){

        cout << "Welcome to Employee Database, Enter an option to continue..." << endl;
        cout << "1) Add an Employee, 2) Delete an Employee, 3) Save Database, 4) Exit" << endl;
        int input;
        cin >> input;

        string fNameInp;
        string lNameInp;
        int salInp;
        string lNameSearch;
        int i; // for loop in Delete employee case
        bool deleted = false;

        switch (input){
        case 1: //Add
            cout << "1) Add a Manager, 2) Add an Engg, 3) Add a Researcher" << endl;
            int empInput;
            cin >> empInput;



            if (empInput == 1){


                cout << "Enter First Name: ";
                cin >> fNameInp;

                cout << "Enter Last Name: ";
                cin >> lNameInp;

                cout << "Enter Salary: ";
                cin >> salInp;

                cout << "Number of meetings/week: ";
                int meetsInp;
                cin >> meetsInp;

                cout << "Number of holidays/year: ";
                int holsInp;
                cin >> holsInp;

                Manager mEmp(fNameInp, lNameInp, salInp, meetsInp, holsInp);
                Employee &emp = mEmp;
                dBVector.push_back(&mEmp);
                dBVector[dBVector.size()-1]->printEmp();

            }

            else if (empInput == 2){
                cout << "Enter First Name: ";
                cin >> fNameInp;

                cout << "Enter Last Name: ";
                cin >> lNameInp;

                cout << "Enter Salary: ";
                cin >> salInp;

                cout << "Cpp Experience (Y/N): ";
                string cppInp;
                cin >> cppInp;

                cout << "Years of experience: ";
                float expInp;
                cin >> expInp;

                cout << "Engg Type (Chem, Mech, IT): ";
                string typInp;
                cin >> typInp;

                Engg eEmp(fNameInp, lNameInp, salInp, cppInp, expInp, typInp);
                Employee &emp = eEmp;
                dBVector.push_back(&eEmp);
                dBVector[dBVector.size() - 1]->printEmp();

            }

            else if (empInput == 3){
                cout << "Enter First Name: ";
                cin >> fNameInp;

                cout << "Enter Last Name: ";
                cin >> lNameInp;

                cout << "Enter Salary: ";
                cin >> salInp;

                cout << "School of PhD: ";
                string schoolInp;
                cin >> schoolInp;

                cout << "Topic of PhD: ";
                string topImp;
                cin >> topImp;

                Researcher rEmp(fNameInp, lNameInp, salInp, schoolInp, topImp);
                Employee &emp = rEmp;
                dBVector.push_back(&rEmp);
                dBVector[dBVector.size() - 1]->printEmp();

            }
            break;

        case 2: // Delete Emp
            for (int x = 0; x < dBVector.size(); x++){
                dBVector[x]->getLastName();
                cout << endl;
            }

            cout << "Input Last name of the employee to delete: " << endl;
            cin >> lNameSearch;
            for (i = 0; i < dBVector.size(); i++){
                if (dBVector[i]->getLastName() == lNameSearch){
                    dBVector.erase(dBVector.begin() + i);
                    cout << dBVector[i]->getFirstName() << "has been deleted from database";
                    deleted = true;
                    break;
                }
            }
            if (deleted == false && i == dBVector.size()){
                cout << "No Employee with Last Name - " << lNameSearch << " exists in Database." << endl;
            }
            else
                break;

        case 3: //save
            cout << "saving..." << endl;
            break;

        case 4: //exit
            exit = true;
            break;
        }
    }
}
Please Help!

Upvotes: 2

Views: 7567

Answers (2)

Chris Drew
Chris Drew

Reputation: 15334

Firstly, if you want to use polymorphism you need to store pointers in your vector. As the vector is the sole owner of the employees something like std::vector<std::unique_ptr<Employee>> would be suitable.

Edit: I see you have updated the vector to use pointers. But you are storing a pointer to a local stack allocated object, e.g mEmp. This will not work, when the mEmp variable goes out-of-scope at the closing brace the object will be deleted and you will be left with a dangling pointer in your vector that points to a deleted object. Using this dangling pointer is undefined behaviour. You need to allocate the Manager on the heap using new. Then the object will not be deleted when the variable goes out-of-scope but you do need to remember to delete the object when you are done. Something like unique_ptr makes this easy.

Regarding your questions:

  1. Try to minimize explicit casting, especially downcasting. At the point that you store the Employee in the vector it will be implicitly upcast from the derived class to Employee but other than that there is not much need for casting.
  2. You have roughly the right idea when it comes to overriding methods, if you call the virtual printEmp method on an Employee pointer it will call the override in the derived class.

    If you are happy for the user input to be the responsibility of the Employee classes you could simply add a virtual method that initializes the employee using suitable input from the user. But I would be tempted to keep that separate from your domain objects. You need a switch statement on the user choice of employee type anyway so polymorphism doesn't gain you much there.

    If you really want to use polymorphism for the employee creation I would suggest using something like the Abstract Factory pattern.

Here is my suggestion anyway:

#include <vector>
#include <string>
#include <iostream>
#include <memory>

class Employee {
 public:
  Employee(std::string fName, std::string lName, int sal);
  virtual ~Employee();

  virtual void printEmp();

 protected:
  std::string m_fName;
  std::string m_lName;
  int m_sal;
};

class Manager : public Employee {
 public:
  Manager(std::string fName, std::string lName, int sal, int meets, int hols);
  void printEmp() override;

 protected:
  int m_meets;
  int m_hols;
};

Employee::Employee(std::string fName, std::string lName, int sal)
  : m_fName(fName), m_lName(lName), m_sal(sal) {
}

Employee::~Employee() {
}

void Employee::printEmp(){
  std::cout << "First Name: " << m_fName << "\n"
            << "Last Name: " << m_lName << "\n"
            << "Salary: " << m_sal << "\n";
}

Manager::Manager(std::string fName, std::string lName, int sal, int meets, int hols) 
  : Employee(fName, lName, sal), m_meets(meets), m_hols(hols){
}

void Manager::printEmp(){
  Employee::printEmp();
  std::cout << "Meets/Week: " << m_meets << "\n"
            << "Holidays/Year: " << m_hols << "\n";
}

std::unique_ptr<Manager> createManager() {
  std::cout << "Enter First Name: ";
  std::string fNameInp;
  std::cin >> fNameInp;

  std::cout << "Enter Last Name: ";
  std::string lNameInp;
  std::cin >> lNameInp;

  std::cout << "Enter Salary: ";
  int salInp;
  std::cin >> salInp;

  std::cout << "Number of meetings/week: ";
  int meetsInp;
  std::cin >> meetsInp;

  std::cout << "Number of holidays/year: ";
  int holsInp;
  std::cin >> holsInp;
  std::cout << "\n";

  return std::make_unique<Manager>(fNameInp, lNameInp, salInp, meetsInp, holsInp);
}

std::unique_ptr<Employee> createEmployee() {
  int input;  
  std::cout << "1) Add a Manager, 2) Add an Engg, 3) Add a Researcher\n";
  std::cin >> input;    
  switch (input){
    case 1:
      return createManager();   
    default:
      return nullptr;
  }
}

int main() {
  std::vector<std::unique_ptr<Employee>> dBVector; 

  std::cout << "Welcome to Employee Database, Enter an option to continue...\n";
  std::cout << "1) Add an Employee"
            << ", 2) Delete an Employee"
            << ", 3) Save Database"
            << ", 4) Exit\n";
  int input;
  std::cin >> input;

  switch (input){
    case 1:
      dBVector.push_back(createEmployee());
      break;
    default:
      break; // Do nothing
  }

  dBVector.at(0)->printEmp();    
}

Live demo

Upvotes: 2

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16017

You may want to store pointers in the vector to avoid slicing, as others mentioned. Then each employee could have their own input method and ask the right questions to initialize themselves (main would not implement that but only call the respective employee's virtual input function). Input and output are conceptually sysmmetrical operations which hints at implementing them symmetrically, i.e. in this case both as member functions.

Upvotes: 0

Related Questions