// student.cpp - Script 9.7

#include <iostream>

#include <string>

// Declare the Person class.
class Person {
public:
Person(std::string theName);
             
void introduce();
             
protected:
std::string name;
};

// Declare the Teacher class.
// Teacher inherits Person.
class Teacher: virtual public Person {
public:
Teacher(std::string theName, std::string theClass);
             
void teach();
void introduce();
             
protected:
std::string clazz;
};

// Declare the Student class.
// Student inherits Person.
class Student: virtual public Person {
public:
Student(std::string theName, std::string theClass);

void attendClass();
void introduce();

protected:
std::string clazz;
};
          
// Declare the TeachingStudent class.
// TeachingStudent inherits both Student and Teacher.
class TeachingStudent : public Student, public Teacher {
public:
                 
// Constructor.
TeachingStudent(
std::string theName,
std::string classTeaching,
std::string classAttending);
                 
void introduce();
};
                 
// Define the methods.
Person::Person(std::string theName) {
name = theName;
}
                                            
void Person::introduce() {
     std::cout << "Hi I'm " << name << "\n";
     }
     
Teacher::Teacher(std::string theName, std::string theClass)
: Person(theName) {
clazz = theClass;
}

void Teacher::teach() {
std::cout << name << " teaches ";
std::cout << "'" << clazz << "'.\n";
}
     
void Teacher::introduce() {
std::cout << "Hi I'm " <<name << ", and I teach '" << clazz << "'\n";
}
          
Student::Student(std::string theName, std::string theClass): Person(theName) {
clazz = theClass;
}

void Student::attendClass() {
std::cout << name << " attends ";
std::cout << "'" << clazz << "'.\n";
}
     
void Student::introduce() {
std::cout << "Hi I'm " << name << ", and I study '" << clazz << "'\n";
}
          
// TeachingStudent's constructor must invoke
// the constructors of all base classes!
TeachingStudent::TeachingStudent(
std::string theName,
std::string classTeaching,
std::string classAttending)
: Teacher(theName, classTeaching),
Student(theName, classAttending),
Person(theName)
{
}

void TeachingStudent::introduce() {
// Because TeachingStudent inherits two copies of Person's
// attributes (one from Teacher and one from Student),
// we must tell the compiler which copy to use.
std::cout << "Hi I'm " << name << ". I teach '" << Teacher::clazz << "', ";
std::cout << "and I study '" << Student::clazz << "'.\n";
}
     
     
int main() {
    
// Create the objects.
Teacher teacher("Jim", "C++ 101");
Student student("Bob", "C++ 101");
TeachingStudent teachingStudent("Mike", "C++ 101", "Advanced C++");
    
// Make them do things.
teacher.introduce();
teacher.teach();
student.introduce();
student.attendClass();
teachingStudent.introduce();
teachingStudent.teach();
teachingStudent.attendClass();
    
std::cout << "Press Enter or Return to continue.";
std::cin.get();
return 0;
}

Whats confuseing me is the code

TeachingStudent::TeachingStudent(std::string theName, std::sring classTeaching, std::classAttending) :Teacher(theName, classTeaching), Student(theName, classAttending), Person(theName)

theName is passed to the Teacher and Student constructors which is then carried over to the base class: Person(theName) so wouldn't this mean you could call teacher and student::name still? and also call Person::name now? But then that goes against the idea of virtual inheritance...this is so confuseing!! help lol

Edited 5 Years Ago by jimmymack: n/a

I don't think you'd need to call Person, since Teacher and Student constructors already call it.

Since Person is a virtual base class for both Teacher and Student, there is only one implementation in the TeachingStudent class, so even though Student and Teacher constructors are calling the Person constructor, they really are just setting the same name member in Person with the same data. Effectively, writing the member twice. Not a major issue here, but something to look out for in more complex scenarios. Dealing cleanly with virtual base classes is really gnarly at times, which is why I avoid it as much as possible. When I utilize multiple inheritance, I always make sure the base classes are 1) not virtual, and 2) don't have a common ancestor. There are other techniques to get the benefits of multiple virtual inheritance, that are generally safer, such as using the PImpl pattern (Pointer to Implementation).

I think you are stepping into this topic a bit too early in your learning experience. To understand it, you should read this FAQ. And I would also suggest you first read all the parashift FAQs that come before as well (chapters 0 to 25).

>>theName is passed to the Teacher and Student constructors which is then carried over to the base class: Person(theName)

Actually, no. Because of the order of construction, the rule is that all virtual base classes should be initialized before anything else. Since, in this case, a parameter is required to initialize the base class Person, all derived classes have to explicitly call the constructor of Person with a parameter (e.g. theName) in their initialization list. Basically, if you create an object of class Teacher (with two parameters: name and class), then the theName parameter will be carried over to initialize the Person base class. The same if you create an object of class Student. However, if you create an object of class TeachingStudent, in order to be able to construct its virtual base class before anything else, the constructor of Person has to be called with the proper parameter in the initialization list of TeachingStudent (this explains the addition of "Person(theName)" in your "confusing" line of code). When the other (non-virtual) base class constructors are invoked (for Teacher and Student), even though the theName parameter is given to them, it will not be carried over to the virtual base class Person, since that base class constructor was already invoked before the constructors of the non-virtual bases (Teacher and Student). And, of course, constructors are only invoked once and exactly once.

>> you could call teacher and student::name still?

Yes, but they will refer to the same variable (they will no longer refer to two separate data members as was the case without virtual inheritance). Think of it as a way to merge the base classes Person of both Teacher and Student into a single, unique base class Person.

>>and also call Person::name now?

Again, calling Person::name == Student::name == Teacher::name == TeachingStudent::name == name. The 'name' data member is now unique regardless of from where it is accessed.

>>But then that goes against the idea of virtual inheritance

The purpose of virtual inheritance is to allow for base-classes to, even though they appear more than once in the class hierarchy, they are instantiated as a single, unique instance in the created object. Making that happen is non-trivial, and the C++ solution does lead to some confusing or counter-intuitive syntax, but it does make sense (btw: many other programming languages that have OOP features don't support this type of multiple inheritance, mainly because it is too hard or confusing, and many programmers prefer other solutions that are often more appropriate).

>>this is so confuseing!!

It surely will be confusing at first, it takes some more understanding of the underlying mechanisms involved with realizing some of the features of object-oriented programming, and it usually is not a topic addressed that early in any C++ learning path. But, luckily, multiple and virtual inheritance is much less common than you might think (but they are easily abused). So, don't worry too much at this point if that is not easy to grasp, focus on the more important aspects of OOP, like (single) inheritance, encapsulation, abstraction, and polymorphism (virtual member functions), and come back to this issue when you feel more comfortable with it.

Edited 5 Years Ago by mike_2000_17: n/a

Actually, no. Because of the order of construction, the rule is that all virtual base classes should be initialized before anything else. Since, in this case, a parameter is required to initialize the base class Person, all derived classes have to explicitly call the constructor of Person with a parameter (e.g. theName) in their initialization list. Basically, if you create an object of class Teacher (with two parameters: name and class), then the theName parameter will be carried over to initialize the Person base class. The same if you create an object of class Student. However, if you create an object of class TeachingStudent, in order to be able to construct its virtual base class before anything else, the constructor of Person has to be called with the proper parameter in the initialization list of TeachingStudent (this explains the addition of "Person(theName)" in your "confusing" line of code). When the other (non-virtual) base class constructors are invoked (for Teacher and Student), even though the theName parameter is given to them, it will not be carried over to the virtual base class Person, since that base class constructor was already invoked before the constructors of the non-virtual bases (Teacher and Student). And, of course, constructors are only invoked once and exactly once.

Good call. It has been so long since I did any virtual inheritance like this that I forgot the fact that the virtual base class (Person) needs to be initialized by the derived class which encompasses the other classes (TeachingStudent <- Student and Teacher) directly derived from the virtual base (Student and Teacher <- Person). As you so correctly noted Mike, this is confusing even for experienced programmers.

Thanks for the replies. I'm following a book and have understood everything up until now but I'm finding it hard to get my head around inheritance. I feel I partly understand it but not fully. I will read all the faqs - thanks a lot for the link Mike. I think I'm also going to invest in a few books as one obviously isn't enough. I have a passion for programming, I love it.. even though Iv'e really only just begun.. I have a thirst for knowledge but it is HARD!!!!! lol again thanks for the replies!!

This article has been dead for over six months. Start a new discussion instead.