简体   繁体   中英

How can I implement derived-class constructors by reusing base-class constructors?

Let us look at the following class:

ProjectManager.hh

#ifndef INPUT_CPP_FILES_PROJECT_MANAGER_HH
#define INPUT_CPP_FILES_PROJECT_MANAGER_HH

#include <string>

#include "Employee.hh"
#include "Programmer.hh"
#include "Tester.hh"

namespace OrganizationNamespace {
    class ProjectManager : public Programmer, public Tester {
    public:
        ProjectManager():Employee(), Programmer(), Tester()
        {}

        explicit ProjectManager(const std::string& id):Employee(id), Programmer(), Tester()
        {}

        ProjectManager(std::string&id, std::string &name):Employee(id, name), Programmer(), Tester()
        {}

        ProjectManager(std::string &id, std::string &name, std::string &programming_language):Employee(id, name), Programmer(programming_language), Tester()
        {}

        ProjectManager(std::string &id, std::string &name, std::string &programming_language, std::string &tool):Programmer(programming_language), Tester(tool),Employee(id, name)
        {}

        ProjectManager(ProjectManager const & pm)
        {
            this->id_ = pm.id_;
            this->name_ = pm.name_;
            this->programming_language_ = pm.programming_language_;
            this->tool_ = pm.tool_;
        }

        void print() const {
            std::cout << "(" << Employee::id_ << ", " << Employee::name_ << ", " << Programmer::programming_language_ << "," << Tester::tool_ << ")"
                      << std::endl;
        }
    };
}
#endif //INPUT_CPP_FILES_PROJECT_MANAGER_HH

main.cpp

#include "Employee.hh"
#include "Programmer.hh"
#include "ProjectManager.hh"

int main()
{
    std::string id  = "id";
    std::string name  = "name";
    std::string programming_language  = "programming-language";
    std::string testing_tool  = "testing-tool";

    OrganizationNamespace::ProjectManager pm(id, name, programming_language, testing_tool);
    pm.print();
}

Output

C:\Users\pc\source\repos\input_cpp_files\cmake-build-debug\input_cpp_files.exe
(id, name, ,)

Process finished with exit code 0

As we can see the program is not giving the correct output.

The expected output is:

C:\Users\pc\source\repos\input_cpp_files\cmake-build-debug\input_cpp_files.exe
(id, name, programming-language, testing-tool)

Process finished with exit code 0

How can I implement constructors in ProjectManager so that they give the correct output?




Additional Source Code

Employee.hh

#ifndef INPUT_CPP_FILES_EMPLOYEE_HH
#define INPUT_CPP_FILES_EMPLOYEE_HH

#include <iostream>
#include <string>

namespace OrganizationNamespace {
    class Employee {
    protected:
        std::string id_;
        std::string name_;

    public:
        Employee() : id_ (""), name_("") {}

        Employee(const std::string &id) : id_(id), name_("") {}

        Employee(const std::string &id, const std::string &name) : id_(id), name_(name) {}

        Employee(Employee const & emp)
        {
            this->id_ = emp.id_;
            this->name_ = emp.name_;
        }

        void print() const
        {
            std::cout << "(" << id_ << ", " << name_ << ")" << std::endl;
        }
    };
}
#endif //INPUT_CPP_FILES_EMPLOYEE_HH

Programmer.hh

#ifndef INPUT_CPP_FILES_PROGRAMMER_HH
#define INPUT_CPP_FILES_PROGRAMMER_HH

#include "Employee.hh"

namespace OrganizationNamespace {
    class Programmer : public virtual Employee {
    protected:
        std::string programming_language_;

    public:
        Programmer() : Employee(), programming_language_("") {}

        Programmer(std::string id) : Employee(id) {}

        Programmer(std::string id, std::string name) : Employee(id, name) {}

        Programmer(std::string id, std::string name, std::string programming_language) : Employee(id, name),
                                                                                         programming_language_(
                                                                                                 programming_language) {}

        void print() const {
            std::cout << "(" << id_ << ", " << name_ << ", " << programming_language_ << ")" << std::endl;
        }
    };
}

#endif //INPUT_CPP_FILES_PROGRAMMER_HH

Tester.hh

#ifndef INPUT_CPP_FILES_TESTER_HH
#define INPUT_CPP_FILES_TESTER_HH

#include <string>

#include "Employee.hh"

namespace OrganizationNamespace {

    class Tester : public virtual Employee {
    protected:
        std::string tool_;

    public:
        Tester() : Employee(), tool_("") {}

        Tester(std::string id) : Employee(id) {}

        Tester(std::string id, std::string name) : Employee(id, name) {}

        Tester(std::string id, std::string name, std::string tool) : Employee(id, name), tool_(tool) {}

        void print() const {
            std::cout << "(" << id_ << ", " << name_ << ", " << tool_ << ")" << std::endl;
        }
    };
}
#endif //INPUT_CPP_FILES_TESTER_HH

The problem is that when you call the ProjectManger cosntructor you also call the overloaded cosntructors of Programmer and Tester , the versions you are calling are those which take as parameter one std::string , those constructors change the value of Employee::id_ , actually you never changed other values. For fixing the problem you must change

ProjectManager(std::string& id, std::string& name, std::string& programming_language, std::string& tool) : 
Employee(id, name), 
Programmer(programming_language), 
Tester(tool) 
{}

in

ProjectManager(std::string& id, std::string& name, std::string& programming_language, std::string& tool) : 
Employee(id, name), 
Programmer("","",programming_language), 
Tester("","",tool) 
{}

Your 4-parameter ProjectManager constructor calls the 1-parameter Programmer constructor, which does not set the programming_language_ member. The same for the Tester constructor.

So neither of the member variables for your two classes get anything other than default initialized to empty strings.

The solution is to pass the proper values in the proper parameters, and construct the base classes in the correct order.

namespace OrganizationNamespace {
    class ProjectManager : public Programmer, public Tester {
    public:
        // ...
        ProjectManager(std::string &id, std::string &name, std::string &programming_language):
            Employee(id, name), Programmer(id, name, programming_language), Tester()
        {}

        ProjectManager(std::string &id, std::string &name, std::string &programming_language, std::string &tool):
            Employee(id, name), Programmer(id, name, programming_language), Tester(id, name, tool)
        {}
        // ...
    };

Even though the id and name parameters won't be used by the Programmer or Tester constructors (because the Employee base class they are passed to is virtual and will be constructed by the most derived object, ProjectManager ), we still pass in those values. There are a couple of reasons for that.

  1. Because we already have these string objects, and the constructors take their parameters as references, we avoid the overhead of constructing temporary string objects that will be unused.
  2. The code does not rely on the assumption that the id and name paramaters are unused. It is possible that a future change to the constructors will make use of one or both parameters. By passing in the expected values that can avoid future bugs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM