1

Let's say that we have class Person with two fields - name and number. Class Student inherits Person and adds another field called averageGrade.

I've defined the operator "<<" for Person and Student and want to be able to have an array of Person, which would also contain Student objects as well. When I want to print an element from the array which happens to be a Student, I want the definition for the operator "<<" that's specific for Student to be called, not the one for Person.

How does one go about doing that?

person.h:

#pragma once
#include <iostream>
#include <string>
using namespace std;


class Person
{
    private:
        string name;
        int number;
    public:
        Person();
        Person(string,int);

        friend ostream& operator<<(ostream& os, const Person& person);
};

person.cpp:

#include "person.h"

Person::Person() : Person("defaultName", 0)
{
}

Person::Person(string name, int number)
{
  this->name = name;
  this->number = number;
}

ostream& operator<<(ostream& os, const Person& person)
{
  os << "Name: " << person.name << endl;
  os << "Number: " << person.number;

  return os;
}

student.h:

#pragma once
#include "person.h"

class Student : public Person
{
  private:
    double averageGrade;
  public:
    Student();
    Student(string, int, double);

    friend ostream& operator<<(ostream& os, const Student& student);
};

student.cpp:

#include "student.h"
Student::Student() : Person()
{
  this->averageGrade = 5.0;
}

Student::Student(string name, int number, double avgGrade) : Person(name, number)
{
  this->averageGrade = avgGrade;
}

ostream& operator<<(ostream& os, const Student& student)
{
  os << (Person) student << endl;
  os << "Average grade: " << student.averageGrade;

  return os;
}

main.cpp:

#include "student.h"

int main()
{

  Person people[10];
  people[0] = Person("Tom", 1);
  people[1] = Student("Greg", 6, 5.74);
  cout << people[0] << endl;
  cout << people[1] << endl; // prints only the name and number, without the grade


  return 0;
}
Mr. Nicky
  • 1,519
  • 3
  • 18
  • 34
  • For run time polymorphism, you can look at virtual. – themagicalyang Nov 09 '16 at 11:41
  • 4
    Give the base class a [`virtual`](http://en.cppreference.com/w/cpp/language/virtual) function like `std::ostream& print(std::ostream& os)`, and have `operator<<` for the base class just call that. You can't make the `<<` itself `virtual`, because it can't be a member function (the stream has to be the first argument). – BoBTFish Nov 09 '16 at 11:42
  • 1
    If you want to have people of different types in a single array, you must store (smart) pointers. What you do now is called "slicing": with `people[1] = Student("Greg", 6, 5.74);` you assign to the `Person` (*not* `Student`!) element copies of the common fields from the right hand side `Student`. – Peter - Reinstate Monica Nov 09 '16 at 12:16

1 Answers1

3

An approach can look simple.

In each class define for example a public or protected virtual function like

virtual std::ostream & out( std::ostream & );

and then write the output operator like

friend std::ostream & operator <<( std::ostream &os, const Person &person )
{
    return person.out( os );
}

and

friend std::ostream & operator <<( std::ostream &os, const Student &student )
{
    return student.out( os );
}

or have only the first operator.

Take into account that you may not declare an array that will store objects of type Person and Students together.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335