Друг И встроенный метод, какой смысл?

В заголовке я вижу, что я не писал себе следующее:

class MonitorObjectString: public MonitorObject {
   // some other declarations
   friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { return(lhs.fVal==rhs.fVal); }

Я не могу понять, почему этот метод объявлен как друг. Я думал, что имеет смысл, если функция определена в другом месте и ей нужен доступ к внутреннему члену класса, но здесь это не так, поскольку оно является встроенным и даже не нуждается в доступе к членам.

Как вы думаете? Является ли "друг" бесполезным?

Ответы

Ответ 1

friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { 
    return(lhs.fVal==rhs.fVal); 
}

называется friend definition. Он определит функцию как функцию, не являющуюся членом пространства имен, окружающего класс, в котором он появляется. Фактически, встроенный избыток: он неявно объявляется встроенным, если это определение друга. Некоторые плюсы и минусы:

  • Это делает оператор не видимым для обычного поиска. Единственный способ, которым вы можете это назвать, - использовать зависимый от аргументов поиск. Это приведет к тому, что пространство имен будет свободным от многих объявлений операторов, видимых нормально. Обратите внимание, что это также отключит возможность вызова его с помощью неявных преобразований в MonitorObjectString (поскольку, если оба типа аргументов не совпадают во время поиска кандидатов, которые будут вызваны, зависящий от аргументов поиск не найдет функцию).
  • Поиск имен начинается в области класса, в котором появляется определение друга. Это означает, что длинные имена типов или другие имена не должны быть выписаны. Просто отсылайте их так же, как и к нормальной функции-члену класса.
  • Так как это друг, функция видит внутренности MonitorObjectString. Но это ни хорошо, ни плохо. Это зависит от ситуации. Например, если есть функции getFVal(), что делает функцию friend довольно бессмысленной. Может использовать getFVal как-то тогда.

Мне нравился стиль определения этого друга операторов, потому что они имеют прямой доступ к членам класса и появляются в определении класса, поэтому я мог бы "все с одним взглядом". Однако в последнее время я пришел к выводу, что это не всегда хорошая идея. Если вы можете (и вы должны) реализовать оператор, используя только публичные функции-члены класса, вы должны сделать его оператором non-friend (и не членом), определенным в том же пространстве имен класса. Он гарантирует, что если вы измените какую-либо реализацию, но сохраните интерфейс класса одинаковым - оператор все равно будет работать, и у вас будет меньше каскадных изменений, потому что вы знаете, что он не может получить доступ к деталям реализации.

Тем не менее, я предпочитаю этот стиль над написанием операторов-членов, потому что функции-операторы в области пространства имен имеют дополнительные особенности симметричного с их аргументами: они не обрабатывают специальную функцию слева, потому что обе стороны являются просто нормальными аргументами, а не объектные аргументы, привязанные к *this. Если либо левая, либо правая сторона относится к типу вашего класса, другая сторона может быть неявно преобразована - независимо от того, слева или справа. Для функций, которые также определены без синтаксиса определения друга (традиционно, в области пространства имен), у вас будет функция выборочного включения заголовков, которые делают эти операторы доступными или нет.

Ответ 2

Грамматически говоря...

Ключевое слово friend все еще необходимо, чтобы сообщить компилятору, что эта функция не является членом класса, EDIT:, а вместо него - функция, не являющаяся членом, которая может видеть частных членов класс.


Однако это могло быть реализовано более чисто:

/* friend */ inline bool operator ==(const MonitorObjectString& rhs) const
{ return fVal == rhs.fVal; }

(Конечно, я предполагаю, что fVal имеет подходящий тип, который можно сравнить, не влияя на его константу.)

Ответ 3

Они не являются взаимоисключающими. "friend" означает, что функция, не являющаяся членом, может обращаться к закрытым членам класса. "inline" означает, что вызов вызова функции отсутствует, тело функции дублируется (в сборке) на каждом сайте вызова.