#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.