0

This is the first time I ask something in a forum, I hope this is the right place to post my question. I'm pretty newb in C++ and I have a problem to solve about inheritance.

#include <iostream>
#include <string>

using namespace std;

class student //base class
{
    public:
        student();
    protected:
        string name;
        int age;
        int votes[10];
};

class schoolclass:public student  //derived class
{
    public:
        schoolclass();
        setStudentInfo();
    setClassinfo();
    private:
        char section;
        int classgrade;
        int nstudents;
        student students[10];
};

int main()
{
    schoolclass mySchoolclass;
}

When I create the object schoolclass, all the public and protected members of the base class are inherited, so basically a student is automatically added in the derived class, if I'm not wrong. Then if I add an array of other 10 students in the derived class, I'll end up with 11 students in mySchoolclass right? So I should add only 9 students in the array to get what I want.

Instead of adding other 9 students, is it possible to inherit an array? What I'm trying to get is to create 10 students instead of 1 during the inheritance. Tell me if I'm missing the point of inheritance, this is a new argument to me.

Forgive my limited and poor english, I tried to write as politely as possible, I hope I clearly explained my problem.

Faith98
  • 13
  • 1
  • 8
    A _schoolclass_ is a _student_. Interesting concept. – user0042 Nov 05 '17 at 11:33
  • Raw arrays are not classes and cannot be inherited. If you want an array of 10 students, use a `std::vector`. – Cheers and hth. - Alf Nov 05 '17 at 11:34
  • 8
    There is composition and inheritance. Composition means "has a", like a school class "has a" teacher, or multiple students. Inheritance means "is a", like in "a student is a person". You are saying that a school class is a student, which I would say is just nonsense. Remove the inheritance and just stick with the member. – nwp Nov 05 '17 at 11:35
  • 2
    For the record you *can* inherit from `std::array` or `std::vector`, but again that would make no logical sense. – nwp Nov 05 '17 at 11:35
  • 4
    I think what you want here is composition and not inheritance. – Gaurav Sehgal Nov 05 '17 at 11:38
  • _@Faith98_ Did you use inheritance to be able to access the `protected` members of `student` from `schoolclass`? That's the wrong approach. Provide `public` setters/getters for your `student` class instead. – user0042 Nov 05 '17 at 11:43
  • Thanks everyone for the fast answers! – Faith98 Nov 05 '17 at 12:30
  • @user0042 I know it seems weird but here in Italy we have a different concept of class/schoolclass: we stick with the same classmates till the graduation, we consider a schoolclass a set of multiple students. Basically the students "are" a class. – Faith98 Nov 05 '17 at 12:30
  • @Cheers and hth. - Alf I didn't cover the vectors yet, but if I can inherit them then I think this is the way to go. – Faith98 Nov 05 '17 at 12:31
  • 2
    No, don't inherit. A school class is not a student. To model a school class use a `std::vector` (or other collection) as a data member. – Cheers and hth. - Alf Nov 05 '17 at 12:34
  • Honestly I used inheritance over composition mainly for learning purposes, I know it might be a bad example but the teacher wanted something useful in school context. I thought inheritance would have been a better choice due to its rigid nature: if inside student I use the private access specifier instead of protected and then I add the array inside schoolclass, I have to add public members inside the student class to change or access them. But I want the accessibility only from the schoolclass public members.I hope I didn't mess up with the syntax. – Faith98 Nov 05 '17 at 12:48

1 Answers1

-2

Here's a commented example which hopefully will show the difference between inheritance and composition.

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


class Person
{
protected:    
    // a person always has a name and a positive age.
    std::string name;
    unsigned int age; 
public:
    // create the person by giving it a name and an age.
    // A person without name and age mustn't exist.
    Person( std::string _name, unsigned int _age )
    {
        // this is the simplified version to assign member variables...
        // for a better version see class teacher
        name = _name;
        age = _age;
    }

    // return the name, but promise to leave the class and its members intact ("const")
    std::string getName() const
    {
        return name;
    }

    // return the age, but promise to leave the class and its members intact ("const")
    unsigned int getAge() const
    {
        return age;
    }

};

class Student : public Person
{
protected:
    // std::vector is an array of variable length.
    // see 
    std::vector<int> votes; 
public:
    Student( std::string _name, unsigned int _age )
        : Person( _name, _age ) // create the base class - we have to tell it the name and age
    {
        // nothing to do here
    }

    std::vector<int> getVotes() const
    {
        return votes;
    }

    // set the whole "votes" array. This changes the class (respectively the "votes" member) so no "const"
    void setVotes( std::vector<int> _votes )
    {
        votes = _votes;
    }

    // this adds a single vote
    void addVote( int _vote )
    {
        votes.push_back( _vote );
    }

    // this returns the current number of votes
    int getNumVotes() const
    {
        return votes.size();
    }

};

class Teacher : public Person
{
protected:
    unsigned int salary; 
public:
    // a teacher is a person and thus has a name and an age, but also a salary
    Teacher( std::string _name, unsigned int _age, unsigned int _salary )
        : Person( _name, _age ) // create the base class - we have to tell it the name and age
        , salary( _salary ) // here the member salary is set - better method than to do it in the block.
    {
        // nothing to do here
    }

    unsigned int getSalary() const
    {
        return salary;
    }

    void setSalary( unsigned int _salary )
    {
        salary = _salary;
    }
};

// a school class is a standalone class, because it is no person, no teacher, no student.
// But it CONTAINS a teacher and some students.
class Schoolclass
{
private:
    Teacher teacher;
    std::vector<Student> students;
public:
    Schoolclass( Teacher _teacher )
        : teacher(_teacher)
    {
    }

    // this adds a student
    void addStudent( Student _student )
    {
        students.push_back( _student );
    }

    std::vector<Student> getAllStudents() const
    {
        return students;
    }

    Teacher getTeacher() const
    {
        return teacher;
    }

    // now let's return all persons in the school class - this includes both teacher and students
    std::vector<Person> getAllPersons() const {
        // create a vector of persons we can later on return.
        std::vector<Person> result;

        // add the teacher first.
        // Although his class is "Teacher", we can add him to a vector or "Person"s, because he
        // is also a person (Teacher inherits Person). This is one of the benefits of inheritance.
        result.push_back(teacher);

        // Then add all children.
        // We can iterate them.
        for( int i = 0; i<students.size(); i++ )
            result.push_back( students[i] );

        // return the vector of persons.
        return result;
    }

}; 

int main() 
{
    // create a teacher first, because the school class can not exist without teacher.
    Teacher mrSmith( "Smith", 36, 2000 );

    // Then create some students.
    Student tim( "Tim", 12 );
    Student laura( "Laura", 13 );
    Student sam( "Sam", 12 );

    // create the school class with the teacher
    Schoolclass schoolclass( mrSmith );

    // add the students.
    schoolclass.addStudent(tim);
    schoolclass.addStudent(laura);
    schoolclass.addStudent(sam);


    // now let's check it. Print the teacher first.
    std::cout << "Teacher: " << schoolclass.getTeacher().getName();
    std::cout << " Age: " << schoolclass.getTeacher().getAge();
    std::cout << " Salary: " << schoolclass.getTeacher().getSalary() << std::endl << std::endl;

    // Then print all the children.
    std::vector<Student> students = schoolclass.getAllStudents();
    for( int i = 0; i<students.size(); i++ )
    {
        std::cout << "Student: " << students[i].getName();
        std::cout << " Age: " << students[i].getAge() << std::endl;
    }

    std::cout << std::endl;

    float averageAge = 0;
    // get all persons from the class.
    std::vector<Person> allPersons = schoolclass.getAllPersons();
    for( int i=0; i<allPersons.size(); i++ )
    {
        averageAge += allPersons[i].getAge();
    }
    averageAge = averageAge / allPersons.size();

    std::cout << "There are " << allPersons.size() << " persons in the class (including the teacher)" << std::endl;
    std::cout << "Their average age is " << averageAge;

    char c;
    std::cin >> c;

}

Note: At some places it's a bit simplified (e.g. as a trained c++ programmer you would of course hand over strings and vectors as const ref)

user2328447
  • 1,807
  • 1
  • 21
  • 27
  • Thanks it's a nice example! Actually it explains very well not just the difference between composition and inheritance, I don't know why someone gave it a negative vote. I still have to understand better the **unsigned** keyword and some standard functions like size() or push_back(), but anyway thanks for the answer! – Faith98 Nov 05 '17 at 14:22
  • -1 again. I’ll explain. Basically this is bad code in an outdated style. Major problems: names starting with an underscore are dangerous; there’s lots of unnecessary copies going on (mainly because of the intentional omission of const&); there’s no reason to show the bad practice of assigning members in the ctor body, even if it would simplify things slightly; the for loops should be [range-for](http://en.cppreference.com/w/cpp/language/range-for). That said the answer *does* provide a useful showcase for inheritance vs. composition. But some major revision is required. – besc Nov 05 '17 at 14:45
  • @Faith98 Don’t be so hasty with accepting an answer. As soon as you accept one, the chances of getting other (maybe better) answers drops dramatically. Give it a day or so at least, and then pick the answer you like best. – besc Nov 05 '17 at 14:45
  • I'm convinced that showing both the member initialization and the assignment by constructor body is pretty useful since I'm new; the example code shows which one is better. I can't say anything about range-for loops, it seems pretty advanced. But why is using underscored variables(_var) a bad practice? Isn't it a name convention? – Faith98 Nov 05 '17 at 15:14
  • @Faith98 Some of those underscored names are reserved. You may get name clashes with the compiler/stdlib implementation. If bugs like that compile it’s usually a nightmare to find them. The rules are a bit tricky, so its easiest to avoid leading underscores altogether. This answer has all the details: https://stackoverflow.com/questions/228783/228797#228797 – besc Nov 06 '17 at 15:34
  • @besc: Simply wrong. None of the names I used are reserved. The naming convention _lowCamelCase is standard compliant in the context of parameter naming of class methods. But I agree it is perhaps not suitable in an answer for a beginner. Yes, there's a lot of unneccessary copies going on due to the simplification (which I mentioned!). Nevertheless it's valid and safe code. Have you ever heared about the concept to simplify stuff for beginners? – user2328447 Nov 07 '17 at 22:08
  • @besc: There is a reason to show member allocation in the constructor body (please, be so kind and avoid 'insider' words like "ctor" when you are talking with beginners, will you?): if you need some kind of initialization of the members, it's unavoidable to assign them in the constructor body. I agree that the example is perhaps not the best, because there is no initialization. – user2328447 Nov 07 '17 at 22:08
  • @besc: especially for std::vector, conventional for-loops are still perfectly fine in many cases (major exception writing templates which handle containers) - that's what this container was made for. And it's much nearer to the C vectors, Faith98 already seems to know a bit. I'd introduce ranged-for as soon as things like size(), push_back() etc. and especially const ref were understood. All in all I am pretty shocked about the obvious inability to differ between production code and a simplified beginner's example. – user2328447 Nov 07 '17 at 22:10
  • I suppose we’ll have to agree to disagree on this one. From a simple note at the end how is a beginner supposed to judge which exact places in the code are dumbed down? Imo the only way to write an educational example is to shorten it until it shows exactly what it’s intended to teach and not a single line more, and make it the best C++ you can possibly write. Dumbing down the lines that have to be there risks creating/enforcing bad habits for a very questionable advantage. – besc Nov 08 '17 at 15:51