перегрузка << операторов и унаследованных классов

#c #inheritance #operators #polymorphism

#c #наследование #операторы #полиморфизм

Вопрос:

У меня есть базовый класс, а затем несколько производных классов. Я хотел бы перегрузить оператор «<<» для этих производных классов. Для обычных операторов, то есть ‘ ‘, виртуальные функции делают свое дело. То, что я понимаю как стандартное соглашение, заключается в том, чтобы объявить

 friend ostreamamp; operator<<(ostreamamp; out, MyClassamp; A);
  

внутри моего класса, а затем определите функцию после класса. Априори я бы подумал, что добавление virtual к приведенному выше определению заставило бы его работать, но после некоторых размышлений (и ошибок моего компилятора) Я понимаю, что в этом нет особого смысла.

Я попробовал другой подход к тестовому случаю, где все члены класса являются общедоступными. Например:

 class Foo{
 //bla
};

ostreamamp; operator<<(ostreamamp; out, Fooamp; foo){
  cout << "Foo" << endl;
  return foo;
}

class Bar : public Foo{
 //bla
};

ostreamamp; operator<<(ostreamamp; out, Baramp; bar){
  cout << "Bar" << endl;
  return bar;
}

///////////////////////

Bar bar = Bar();
cout << bar << endl; // outputs 'Foo', not 'Bar' 
  

Таким образом, в некотором роде это «полиморфизм испортился» — вызывается оператор базового класса<<, а не оператор производного класса. В приведенном выше примере, как мне вызвать правильный оператор для производного класса? И в более общем плане, если в моем классе есть закрытые члены, которые я хочу защитить, как я могу исправить перегрузку оператора при использовании ключевого слова friend?

Ответ №1:

Вы можете использовать виртуальную вспомогательную функцию. Вот совершенно непроверенный пример, поэтому извините за любые синтаксические ошибки:

 virtual ostreamamp; Foo::print(ostreamamp; out) const {
    return out << "Foo";
}

virtual ostreamamp; Bar::print(ostreamamp; out) const {
    return out << "Bar";
}

// If print is public, this doesn't need to be a friend.
ostreamamp; operator<<(ostreamamp; out, const Fooamp; foo) {
    return foo.print(out);
}
  

Редактировать: Очищено в соответствии с предложениями @Omnifarious.

Комментарии:

1. Я думаю, что у этого есть два недостатка. Один недостаток — огромный недостаток, а другой — незначительный недостаток. Сначала огромный недостаток… вы никогда не должны незаметно вставлять endl . endl вызывает сброс потока, что в некоторых обстоятельствах может стать большой проблемой с производительностью. Использовать 'n' . Он гарантированно будет таким же переносимым (фактически, endl определяется в терминах вывода 'n' и не требует дополнительных затрат). Во-вторых, я бы сделал это: return out << "Foon"; . На ощупь немного чище. Это концептуально превращает все это в длинную цепочку << операций.

2. @Omnifarious Я бы никогда не стал endl перегружать operator<< себя. Я просто следовал коду операции.

3. Кроме того, я думаю, что было ошибкой вообще помещать endl манипулятор IOStream в язык. flush Манипулятор уже существует, и endl манипулятор объединяет две операции, которые всегда должны быть разными.

4. @Omnifarious Интересно, я даже не знал, что такое существует.

Ответ №2:

Обычно вы просто создаете полиморфный print метод в базовом классе, который вызывается одной свободной функцией friend.

Комментарии:

1. Если печать общедоступна, то мы можем избавиться от друга.

Ответ №3:

Создайте operator<< свободную функцию, которая перенаправляет вызов virtual методу класса Foo .

Посмотрите на это в действии.

Ответ №4:

При соответствующих исправлениях кода ваш код работает нормально; ничего не нужно делать:

 ostreamamp; operator<<(ostreamamp; out, Fooamp; foo) {
  out << "Foo" << endl;  // 'out' and not 'cout'
  return out;  // returns 'out' and not 'foo'
}

ostreamamp; operator<<(ostreamamp; out, Baramp; bar) {
  out << "Bar" << endl;  // 'out' and not 'cout'
  return out;  // returns 'out' and not 'bar'
}
  

ДЕМОНСТРАЦИЯ. Для доступа к private членам вы можете сделать эту функцию как friend в желаемом class .

Комментарии:

1. Интригующе. В моем фактическом коде я правильно использовал out вместо cout , но это все равно не сработало. Должно быть, происходит что-то неуловимое, что не улавливается классами filler //bla.