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

// prédéclarations des « objets » avec lesquels les QuelqueChose peuvent interagir
class Personne;
class Chien;
class Mur;

// ==== modélisation des interactions, ici des rencontres
class QuelqueChose
{
public:
  virtual ~QuelqueChose() = default;

  // ici, toutes les interactions souhaitées
  virtual void interagit_avec(Personne const&) const = 0;
  virtual void interagit_avec(Chien    const&) const = 0;
};

// ==== ce qui peut rencontrer quelque chose (et a aussi un nom)
// -- ici tout EtreVivant peut être rencontrable (donc « est un » QuelqueChose)
// -- mais ce n'est pas strictement nécessaire
class EtreVivant : public QuelqueChose
{
public:
  EtreVivant(const string& nom_) : nom(nom_) {}
  virtual void rencontre(QuelqueChose const& autre) = 0;
  const string nom;
};

// ----------------------------------------------------------------------
class Personne : public EtreVivant
{
public:
  using EtreVivant::EtreVivant;

  void rencontre(QuelqueChose const& autre) override
  { autre.interagit_avec(*this); }

  void interagit_avec(Personne const& p) const override
  { cout << p.nom << " salue " << nom << endl; }
  void interagit_avec(Chien    const& c) const override;
};

// ----------------------------------------------------------------------
class Chien : public EtreVivant
{
public:
  using EtreVivant::EtreVivant;

  void rencontre(QuelqueChose const& autre) override
  { autre.interagit_avec(*this); }

  void interagit_avec(Personne const& p) const override
  { cout << p.nom << " caresse " << nom << endl; }
  void interagit_avec(Chien    const& c) const override
  { cout << c.nom << " renifle " << nom << endl; }
};

// ----------------------------------------------------------------------
void Personne::interagit_avec(Chien const& c) const
{ cout << c.nom << " aboye (sur " << nom << ")" << endl; }

// ----------------------------------------------------------------------
class Mur : public QuelqueChose
// un mur est rencontrable (donc « est un » QuelqueChose),
// mais n'est pas un EtreVivant
{
  void interagit_avec(Personne const& p) const override
  { cout << p.nom << " heurte un mur !" << endl; }
  void interagit_avec(Chien    const& c) const override
  { cout << c.nom << " se soulage." << endl; }
};
  
// ======================================================================
int main()
{
  Personne p1("Dupond");
  Personne p2("Dupont");
  Mur mur; // un mur mûr qui ne fait pas de bruit
  Chien milou("Milou");

  p1.rencontre(p2);
  p1.rencontre(milou);
  milou.rencontre(p1);
  p1.rencontre(mur);
  milou.rencontre(mur);

  // mur.rencontre(p1); // pas possible, un mur ne peut pas rencontrer quelque chose
  
  return 0;
}
