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;
}
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.)
Your declaration of array
is fine, but you would then print the elements with:
std::cout << *arr[0] << std::endl;
std::cout << *arr[1] << std::endl;
The problem with initializing the array, is that by default, base classes are private. Fix that with:
class Student : public Person ...