Настройка поддержки cout в структурах с несколькими уровнями наследования

#include <iostream>
#include <string>

struct Printable abstract
{
 friend std::ostream& operator<<(std::ostream& cout, const Printable& obj)
 {
  obj.print(cout);

  return cout;
 }

 virtual void print(std::ostream& cout) const = 0;
};

struct VirtualBase abstract : public Printable
{
 //stuff
};

struct Named abstract : public Printable
{
 std::string name;

 void print(std::ostream& cout) const
 {
  cout << "Name: " << name;
 }
};

struct DerivedA : public VirtualBase
{
 void print(std::ostream& cout) const
 {
  cout << "DerivedA";
 }
};

struct DerivedB : public VirtualBase, public Named
{
 void print(std::ostream& cout) const
 {
  cout << "DerivedB";

  dynamic_cast<const Named*>(this)->print(cout);
  //Is there a better way to call Named::print?
 }
};

Поскольку DerivedB наследует VirtualBase и Named, и оба они наследуют Printable, я не могу использовать DerivedB с cout. Каков наилучший способ обеспечить поддержку печати на нескольких уровнях иерархии наследования? Кроме того, что было бы самым простым способом вызвать Named::print в печати производного класса?

3 ответа

Решение

Проблема в том, что DerivedB это VirtualBase (который является Printable) и Named (который является Printable), Итак operator<< пытается конвертировать DerivedB к Printable, но не могу опускать руки, потому что это два Printable объекты, и он не знает, к чему опускаться. Так как вы хотите только DerivedB происходить от одного Printable объект, вы должны использовать виртуальное наследование.

   normal inheritance:            virtual inheritance:
Printable      Printable                 Printable
   |               |                    /        \
VirtualBase      Named          VirtualBase      Named
       \         /                     \         /
        DerivedB                         DerivedB

Что делается просто:

struct Named abstract : virtual public Printable
struct VirtualBase abstract : virtual public Printable

Обратите внимание, что класс с виртуальным наследованием больше и немного медленнее, чем без него, но, с другой стороны, C++ является одним из немногих языков, которые могут это делать вообще.

Вы просто должны определить, какие print Вы хотите позвонить с оператором разрешения области (::) вот так:

Named::print(cout);

Хорошо, я нашел способ исправить это путем шаблонной перегрузки оператора:

 template<typename P> friend std::ostream& operator<<(std::ostream& cout, const P& obj)
 {
  obj.print(cout);

  return cout;
 }

Я не уверен, насколько это стабильно.

Другие вопросы по тегам