Какие операторы следует объявлять друзьями?
В некоторых книгах и часто в Интернете я вижу рекомендации типа "operator==
должен быть объявлен как друг".
Как я должен понимать, когда оператор должен быть объявлен как друг и когда он должен быть объявлен как член? Каковы операторы, которые чаще всего должны быть объявлены друзьями помимо ==
и <<
?
Ответы
Ответ 1
Это действительно зависит от того, будет ли класс находиться слева или справа от вызова operator==
(или другого оператора). Если класс будет находиться в правой части выражения и не обеспечивает неявное преобразование в тип, который можно сравнить с левой стороной, вам нужно реализовать operator==
как отдельную функцию или как a friend
класса. Если оператору необходимо получить доступ к данным частного класса, он должен быть объявлен как friend
.
Например,
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
};
позволяет сравнить сообщение со строкой
Message message("Test");
std::string msg("Test");
if (message == msg) {
// do stuff...
}
но не наоборот
if (msg == message) { // this won't compile
Вам нужно объявить друга operator==
внутри класса
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
friend bool operator==(const std::string& lhs, const Message& rhs);
};
или объявить неявный оператор преобразования соответствующему типу
class Message {
std::string content;
public:
Message(const std::string& str);
bool operator==(const std::string& rhs) const;
operator std::string() const;
};
или объявить отдельную функцию, которая не обязательно должна быть другом, если она не имеет доступа к данным частного класса.
bool operator==(const std::string& lhs, const Message& rhs);
Ответ 2
Когда у вас есть ваши операторы вне класса, оба параметра могут участвовать в неявных преобразованиях типов (тогда как с операторами, определенными в теле класса, могут использоваться только правые операнды). Как правило, это преимущество для всех классических двоичных операторов (т.е. ==
, !=
, +
, -
, <<
,...).
Конечно, вы должны только объявлять операторы friend
вашего класса, если вам нужно, а не если они вычисляют их результат исключительно на основе публичных членов класса.
Ответ 3
Как правило, только операторы, которые реализуются как свободные функции, которые действительно нуждаются в доступе к закрытым или защищенным данным того класса, в котором они работают, должны быть объявлены друзьями, иначе они должны быть просто не-дружескими функциями, не являющимися членами.
Как правило, единственными операторами, которые я реализую как функции-члены, являются те, которые принципиально асимметричны и где операнды не имеют эквивалентных ролей. Те, которые я стараюсь реализовать в качестве членов, это те, которые должны быть членами: простое назначение, ()
, []
и ->
вместе с составными операторами присваивания, унарные операторы и, возможно, некоторые перегрузки <<
и >>
для классов, которые сами являются потоковыми или потоковыми классами. Я никогда не перегружаю &&
, ||
или ,
.
Все другие операторы, которые я использую как свободные функции, предпочитаю использовать открытый интерфейс классов, на которых они работают, возвращаясь к друзьям только там, где это необходимо.
Сохранение операторов, таких как !=
, ==
, <
, +
, /
и т.д. как функции, не являющиеся членами, позволяет одинаково обрабатывать левый и правый операнды относительно неявных последовательностей преобразования, что помогает для уменьшения числа удивительных асимметрий.
Ответ 4
ЦИКЛИЧЕСКАЯ ПРОВЕРКА ИЗБЫТОЧНОСТИ
перейти к этому и показать эту программу и дать ответ