Mr. Nicky Mr. Nicky - 24 days ago 4
C++ Question

C++ how to invoke a child's operator<<(ostream) from an array/collection of base elements

Say I have a Person class with a field string name. Now let's have a class Student deriving Person with a field int avgGrade. Both classes have the operator<< defined.

I want to be able to store in an array or similar structure elements of type Person, but also be able to store derived class objects in it. I'll be later traversing the elements and want to use the operator<< and actually call that specific object's definition for that operator, instead of always the base version of it.

How would I go about doing that? In what structure and what kind type of elements should I store?

Here's the current collection of classes:

Person.h:

#pragma once
#include <iostream>
class Person
{
private:
std::string name;
public:
Person();
Person(std::string);

friend std::ostream& operator<<(std::ostream& os, const Person& obj);
}


Person.cpp:

#include "Person.h"

Person::Person() : Person("default") { }


Person::Person(std::string name)
{
this->name = name;
}

std::ostream& operator<<(std::ostream& os, const Person& obj)
{
os << "Name: " << obj.name;
return os;
}


Student.h:

#pragma once
#include "Person.h"
#include <iostream>

class Student : Person
{
private:
double avgGrade;
public:
Student();
Student(const std::string cs, const double avg_grade);

friend std::ostream& operator<<(std::ostream& os, const Student& obj);
};


Student.cpp:

#include "Student.h"

Student::Student() : Student("default", 4) { }


Student::Student(const std::string cs, const double avg_grade)
: Person(cs),
avgGrade(avg_grade)
{
this->avgGrade = avg_grade;
}

std::ostream& operator<<(std::ostream& os, const Student& obj)
{
os << (Person)obj << std::endl;
os << "Average grade: " << obj.avgGrade;
return os;
}


Demo.cpp:

#include "Person.h"
#include "Student.h"
#include <iostream>

int main(int argc, char* argv[])
{
Person p("john");
Student s("johana", 5);


Person* arr[2];
arr[0] = &p;
arr[1] = &s; // Error: conversion to inaccessible base class "Person" is not allowed

std::cout << arr[0] << std::endl;
std::cout << arr[1] << std::endl;


return 0;
}

Answer

The general solution to this sort of problem is to declare:

inline std::ostream& operator<<(std::ostream& str, const Base& o)
{
    o.print(str);
    return str;
}

as a non-member function, then:

    virtual void print(std::ostream& str);

in Base, and override in Derived as required. (The Derived version may well start with Base::print(str); to call the base version.)

Comments