друг И встроенный метод, какой в этом смысл?
Вопрос
В шапке вижу, что не сам себе писал следующее:
class MonitorObjectString: public MonitorObject {
// some other declarations
friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { return(lhs.fVal==rhs.fVal); }
Я не могу понять, почему этот метод объявлен как друг.Я подумал, что было бы разумно, если бы функция была определена в другом месте и ей требовался доступ к внутреннему члену класса, но в данном случае это не тот случай, поскольку она встроена и даже не требует доступа к членам.
Что вы думаете?«Друг» бесполезен?
Решение
friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) {
return(lhs.fVal==rhs.fVal);
}
называется friend definition
.Он определит функцию как функцию, не являющуюся членом пространства имен, окружающего класс, в котором она появляется.На самом деле, строка там избыточна:Оно неявно объявляется встроенным, если это определение друга.Некоторые плюсы и минусы этого:
- Это делает оператора невидимым для обычного поиска.Единственный способ вызвать его — использовать поиск, зависящий от аргумента.Это позволит сохранить пространство имен свободным от множества объявлений операторов, видимых в обычном режиме.Обратите внимание, что это также отключит возможность ее вызова с использованием неявных преобразований в MonitorObjectString (поскольку, если оба типа аргументов не совпадают во время поиска кандидатов для вызова, поиск, зависящий от аргумента, не найдет функцию).
- Поиск имен начинается в области класса, в котором появляется определение друга.Это означает, что не нужно записывать длинные имена типов или другие имена.Просто обратитесь к ним, как к обычной функции-члену класса.
- Поскольку это друг, функция видит внутренности
MonitorObjectString
.Но это не хорошо и не плохо.Это зависит от ситуации.Например, если есть функцииgetFVal()
делать функцию другом довольно бессмысленно.Могли бы использоватьgetFVal
ну и тогда.
Раньше мне нравился этот стиль определения друзей, потому что они имеют прямой доступ к членам класса и появляются в определении класса - так что я мог иметь «все одним взглядом».Однако недавно я пришел к выводу, что это не всегда хорошая идея.Если вы можете (и должны) реализовать оператор исключительно с использованием общедоступных функций-членов класса, вам следует сделать его оператором, не являющимся дружественным (и не являющимся членом), определенным в том же пространстве имен класса.Он гарантирует, что если вы измените некоторую реализацию, но сохраните интерфейс класса прежним, оператор все равно будет работать, и у вас будет меньше каскадных изменений, поскольку вы знаете, что он не может получить доступ к деталям реализации.
Однако, я предпочитаю этот стиль написанию операторов-членов, поскольку операторные функции в области пространства имен имеют дополнительные возможности симметричности своих аргументов:Они не рассматривают левую часть как нечто особенное, поскольку обе стороны являются обычными аргументами, а не аргументами объекта, которые привязаны к *this
.Если левая или правая сторона соответствует типу вашего класса, другую сторону можно преобразовать неявно — независимо от того, левая она или правая.Для функций, которые также определены без синтаксиса определения друзей (традиционно, в области пространства имен), у вас будет возможность выборочного включения заголовков, которые делают эти операторы доступными или нет.
Другие советы
Они не являются взаимоисключающими.«друг» означает, что функция, не являющаяся членом, может получить доступ к закрытым членам класса.«встроенный» означает, что вызов функции отсутствует, тело функции дублируется (в ассемблере) на каждом месте вызова.
Грамматически говоря...
А friend
ключевое слово по-прежнему необходимо, чтобы сообщить компилятору, что эта функция не является членом класса, РЕДАКТИРОВАТЬ: но вместо этого функция, не являющаяся членом, которая может видеть частные члены класса.
Однако это можно было бы реализовать более чисто следующим образом:
/* friend */ inline bool operator ==(const MonitorObjectString& rhs) const
{ return fVal == rhs.fVal; }
(Конечно, я предполагаю fVal
имеет подходящий тип, который можно сравнивать, не затрагивая его константность.)