Search code examples
c++inheritanceconstructordiamond-problem

constructor not Inheriting


I was tryiing to inherit a comstructor from base class to the a derived class which was derived from another derived class of the base class. but it says " no deafault constructor exists for class Waiverstudents " after inheriting the base class constructor.


class Student
{
    private:
        string StudentName, StudentID, CGPA , UpcomingSemester , UpcomingSemesterCredits;

    public:
        Student (string a, string b, string c , string d, string e)
        {
            StudentName= a;
            StudentID=b;
            CGPA=c;
            UpcomingSemester=d;
            UpcomingSemesterCredits=e;
        }
        string DisplayStudent()
        {
            cout<<StudentName<<endl<<StudentID<<endl<< CGPA <<endl<< UpcomingSemester <<endl<< UpcomingSemesterCredits;
        }
        

};


class ScholarshipStudents: virtual public Student
{
    private:
        float ScholarshipPercent = 0.3;
        float tution_fee;
    public:
        ScholarshipStudents(string a, string b, string c , string d, string e) : Student(a,b,c,d,e)
        {}
       float TutionFee(int a)
       {
            tution_fee=(a*ScholarshipPercent);
            return tution_fee;
       }

        string DisplayScholarshipStudent()
        {
            DisplayStudent();
            cout<<tution_fee;
        }

};

class WaiverStudents: virtual public Student
{
    private:
        float Waiverpercent = 0.15;
        float tution_fee;
    public:
        WaiverStudents (string a, string b, string c , string d, string e) : Student(a,b,c,d,e)
        {}
       float WaiverTuitionFee(int a )
       {
            tution_fee =(a*Waiverpercent); 
       }
       string DisplayWaiverStudent()
        {
            DisplayStudent();
            cout<<tution_fee;
        }
};


class SpecialStudents : public WaiverStudents , public ScholarshipStudents 
{
    public:
        SpecialStudents(string a, string b, string c , string d, string e): Student(a,b,c,d,e) 
        {} //in this line it shows error. " no deafault constructor exists for class Waiverstudents " 
        string DisplaySpecialStudent()
        {
            DisplayStudent();
        }
};

I tried This

SpecialStudents(string a, string b, string c , string d, string e): ScholarshipStudents(a,b,c,d,e):WaiverStudents(a,b,c,d,e)

same thing happened.

please help me. to overcome the situation. why the I cant inherit the the constructor


Solution

  • There are several problems in your code so I'm going to provide a quick solution to the immediate problem in your question. The problem that you are experiencing is because of the way that virtual inheritance works. In virtual inheritance the most derived type (SpecialStudents in your code) calls the constructor in the base class (Student in your code). It must also call a constructor in each of the other classes it derives from (ScholarshipStudents and WaiverStudents). Since you are only invoking the constructor of SpecialStudents the compiler will implicitly try to invoke the default constructor of all other classes it inherits from and since those constructors do not exist you receive an error.

    To fix this you have two options. The first is to explicitly invoke the constructors of each of the classes SpecialStudents and the second provide a default constructor in ScholarshipStudents and WaiverStudents. The most optimal choice in this case it to provide default constructors in those classes but make them protected to prevent them from being called except during the creation of a derived type.

    class Student
    {
        // ... Other code ...
    
    protected:
        // Default constructor that handles initialization specific to this class.
        Student() = default;
    };
    
    class ScholarshipStudents
    {
        // ... Other code ...
    
    protected:
        // Default constructor that handles initialization specific to this class.
        ScholarshipStudents() = default;
    };
    
    class WaiverStudents
    {
        // ... Other code ...
    
    protected:
        // Default constructor that handles initialization specific to this class.
        WaiverStudents() = default;
    };
    

    And finally your most derived type only needs to explicitly call the constructor in Student

    class SpecialStudents : public WaiverStudents, public ScholarshipStudents
    {
    public:
        SpecialStudents(string a, string b, string c, string d, string e)
            : Student(a, b, c, d, e)
        {}
    };
    

    Please be aware that there are several other problems in your code and the use of virtual inheritance to approach this problem is an incredibly terrible idea. What happens if you add another tuition based fee adjustment? How many different class types will you need to to add to support it. what happens if several more adjustments are added? Now thing about what happens if an existing fee is added or removed from an existing student. Not only will you have to recreate the student but every other reference to that original instance of Student will need to be informed of the change - otherwise they are now working with stale student data.

    A rule of thumb in object oriented development is to prefer composition and aggregation over inheritance and in your case a very compelling example of why. You have created multiple derived types to support something that would be much better served as a simple attribute of student - i.e. a list of fee adjustments that can be applied to their tuition. This way when new fees and adjustments need to be applied to the student those pieces of data can be managed in a more focused and cohesive manner.

    One way to do this is to start by creating a type that represents a fee adjustment. Something that has a name along with the adjustment itself. We're also going to stop using using namespace std because it's an incredibly bad practice. Instead we'll scope in the names we need via std::.

    class FeeAdjustment
    {
    public:
    
        FeeAdjustment(std::string name, float adjustment)
            :
            name_(move(name)),
            adjustment_(adjustment)
        {}
    
    
        const std::string& Name() const
        {
            return name_;
        }
    
        void Set(float newAdjustment)
        {
            adjustment_ = newAdjustment;
        }
    
        float Get() const
        {
            return adjustment_;
        }
    
    private:
    
        const std::string name_;
        float adjustment_ = 0.0f;
    };
    

    Now of course you'll need to update the Student class to manage a list of adjustments as well as the overall tuition the student needs to pay. We can do that by adding a couple of new members to Student like so.

    float Tuition;
    std::vector<FeeAdjustment> TuitionAdjustments;
    

    Now that we have something to track our tuition and keep our list of adjustments in a vector, we'll also want to update the constructor so that it takes the initial tuition fee. We'll also want to update the names of the parameters the constructor takes for clarity.

    Student(
        std::string studentName,
        std::string studentId,
        std::string cgpa,
        std::string upcomingSemester,
        std::string upcomingSemesterCredits,
        float initialTution)
        :
        StudentName(studentName),
        StudentID(studentId),
        CGPA(cgpa),
        UpcomingSemester(upcomingSemester),
        UpcomingSemesterCredits(upcomingSemesterCredits),
        Tuition(initialTution)
    {}
    

    We'll also want to add some new functionality so that we can add a fee adjustment for the student. I'll leave modifying and removing the fees up to you as an exercise.

    void AddFeeAdjustment(std::string name, float feeAdjustment)
    {
        TuitionAdjustments.push_back(FeeAdjustment(name, feeAdjustment));
    }
    

    And finally we'll want to update DisplayStudent so that it includes a list of fees along with the adjusted tuition. To do this we we can just iterate over the list of fees and while displaying them create a total amount to be subtracted from the students tuition.

    void DisplayStudent() const
    {
        std::string output;
        std::cout
            << StudentName << "\n"
            << StudentID << "\n"
            << CGPA << "\n"
            << UpcomingSemester << "\n"
            << UpcomingSemesterCredits << "\n"
            << "Tuition = " << Tuition << "\n";
    
        float tuitionAdjustments = 0.0f;
        for (const auto& adjustmentDetails : TuitionAdjustments)
        {
            const float adjustment(Tuition * adjustmentDetails.Get());
            std::cout
                << "Tution adjustment for " << adjustmentDetails.Name()
                << " = " << std::to_string(Tuition * adjustmentDetails.Get()) << "\n";
            tuitionAdjustments += adjustment;
        }
    
        std::cout << "Tuition (Adjusted) = " << (Tuition - tuitionAdjustments) << "\n";
    }
    

    Now to do some quick validation we'll just create an instance, add some fee adjustments, and display the student.

    Student student("Captain Obvlious", "777", "5.0", "Purple", "100", 10000.0f);
    student.AddFeeAdjustment("Scholarship", .3f);
    student.AddFeeAdjustment("Waiver", .15f);
    
    student.DisplayStudent();
    

    Which gives us the following output:

    777
    5.0
    Purple
    100
    Tuition = 10000
    Tution adjustment for Scholarship = 3000.000000
    Tution adjustment for Waiver = 1500.000000
    Tuition (Adjusted) = 5500```