创建基类对象的向量并将派生类对象存储在其中

Posted

技术标签:

【中文标题】创建基类对象的向量并将派生类对象存储在其中【英文标题】:Create a vector of base class objects and store derived class objects within 【发布时间】:2015-01-19 16:59:59 【问题描述】:

我正在尝试创建一个员工数据库(员工向量)。有3种类型的员工,即。 employees 是基类,Manager、Engg 和 Scientist 是派生类。 每个员工都有名字和姓氏。除了名称之外,这三种类型的员工中的每一种都有独特的统计数据,即。经理每周开会次数,而 Engg 有工作经验等等。

我有几个问题 1.我应该将派生对象向上转换为基类还是将基类向下转换为派生类? 2.我如何使用多态性来覆盖方法,因为我希望用户添加一个员工类型,并且根据选择的类型,应该出现相应的输入字段,即。如果是经理,除了名字和姓氏之外,程序还应该要求每周开会?

这是我的类文件

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;
;

这里是实现

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;

这里是主要的

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!

【问题讨论】:

你不能对值使用多态性。您需要使用指针。见***.com/q/7223613/10077 工程师没有会议?经理没有工作经验?我认为您在这里不需要继承。 你应该读到这个:"What is the slicing problem?. 【参考方案1】:

首先,如果你想使用多态,你需要在你的向量中存储指针。由于向量是员工的唯一所有者,因此std::vector&lt;std::unique_ptr&lt;Employee&gt;&gt; 之类的东西是合适的。

编辑:我看到您已经更新了向量以使用指针。但是您正在存储一个指向本地堆栈分配对象的指针,例如mEmp。这不起作用,当mEmp 变量在右括号处超出范围时,该对象将被删除,并且您的向量中将留下一个指向已删除对象的悬空指针。使用这个悬空指针是未定义的行为。您需要使用new 在堆上分配Manager。然后,当变量超出范围时,对象不会被删除,但您确实需要记住在完成后删除对象。像unique_ptr 这样的东西让这很容易。

关于您的问题:

    尽量减少显式转换,尤其是向下转换。在您将Employee 存储在向量中时,它将从派生类隐式向上转换为Employee,但除此之外没有太多需要转换。

    在覆盖方法时,您的想法大致正确,如果您在 Employee 指针上调用虚拟 printEmp 方法,它将在派生类中调用覆盖。

    如果您对用户输入由Employee 类负责,您可以简单地添加一个虚拟方法,该方法使用来自用户的合适输入来初始化员工。但我很想将它与您的域对象分开。无论如何,您都需要一个关于员工类型的用户选择的 switch 语句,因此多态性不会在那里为您带来太多好处。

    如果你真的想使用多态来创建员工,我建议使用抽象工厂模式之类的东西。

这是我的建议:

#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

【讨论】:

谢谢,我会尝试 unique_ptr 但我也希望您再次检查我编辑的代码,以便我更好地了解我的错误。还有你的意思是什么 - 你正在存储一个指向通常不起作用的局部变量的指针谢谢 另外,当我添加员工时,它不会存储在我创建的向量中 我已更新我的答案以尝试使其更清晰,但您可能需要阅读 C++ 中的堆栈分配与堆分配。既然您的问题与首次发布时相比发生了很大变化,您可能需要发布一个新问题以获得最佳反馈。【参考方案2】:

正如其他人提到的,您可能希望将指针存储在向量中以避免切片。然后每个员工都可以有自己的输入法并提出正确的问题来初始化自己(main 不会实现它,而只会调用各自员工的虚拟输入函数)。输入和输出在概念上是对称的操作,暗示对称地实现它们,即在这种情况下都作为成员函数。

【讨论】:

以上是关于创建基类对象的向量并将派生类对象存储在其中的主要内容,如果未能解决你的问题,请参考以下文章

C ++跨类访问基对象向量中的派生对象的引用

将派生类对象存储在基类变量中

将派生类对象存储在基类变量中

制作基类指针的向量并将派生类对象传递给它(多态)

基础对象和继承对象的向量

如何在向量中存储多个类的不同对象?