#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

class Person {
public:
    Person(const string& name) : name_(name) {
        cout << "in constructor of Person" << "\n";
    }
    void Check(shared_ptr<Person> p);
    virtual string person_type() const = 0;

    string Name() {
        return name_;
    }

    void Walk(const string& destination) {
        cout << person_type() << ": " << name_ << " walks to: " << destination << endl;
    }
private:
    string name_;
};


class Student : public Person{
public:
    Student(const string Name, const string FavouriteSong) : Person{Name}, favourite_song(FavouriteSong){}

    string person_type() const override { return "Student"; }

    void Check(shared_ptr<Person> p) {}

    void Learn() {
        cout << "Student: " << person_type() << " learns" << endl;
    }

    void SingSong() {
        cout << "Student: " << person_type() << " sings a song: " << favourite_song << endl;
    }

    void Walk(const string& destination) {
        Person::Walk(destination);
        SingSong();
    }

private:
    string favourite_song;
};


class Teacher : public Person {
public:
    Teacher(const string& Name, const string& subject) : Person{Name}, subject_(subject) {}

    string person_type() const override { return "Teacher"; }

    void Check(shared_ptr<Person> p) {}

    void Teach() {
        cout << "Teacher: " << person_type() << " teaches: " << subject_ << endl;
    }

private:
    string subject_;
};


class Policeman : public Person {
public:
    Policeman(const string& Name) : Person{Name} {}

    string person_type() const override { return "Policeman"; }

    void Check(shared_ptr<Person> p) {
        cout << "Policeman: " << Name() << " checks" << p->person_type() << "." <<
                p->person_type() << "'s name is: " << p->Name() << endl;
    }
};


void VisitPlaces(shared_ptr<Person> person, vector<string> places) {
    for (auto p : places) {
        person->Walk(p);
    }
}


int main() {
    shared_ptr<Person> t = make_shared<Teacher>("Jim", "Math");
    shared_ptr<Person> s = make_shared<Student>("Ann", "We will rock you");
    shared_ptr<Person> p = make_shared<Policeman>("Bob");

//    Teacher t = Teacher("Jim", "Math");
//    Student s = Student("Ann", "We will rick you");
//    Policeman p = Policeman("Bob");

    VisitPlaces(p, {"Moscow", "London"});
    p->Check(s);
    VisitPlaces(s, {"Moscow", "London"});
    return 0;
}
