друг И встроенный метод, какой смысл? - PullRequest
16 голосов
/ 19 декабря 2008

Я вижу в шапке, что я сам не написал следующее:

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

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

Что ты думаешь? «Друг» бесполезен?

Ответы [ 3 ]

36 голосов
/ 19 декабря 2008
friend inline bool operator==(MonitorObjectString& lhs, MonitorObjectString& rhs) { 
    return(lhs.fVal==rhs.fVal); 
}

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

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

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

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

7 голосов
/ 19 декабря 2008

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

7 голосов
/ 19 декабря 2008

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

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


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

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

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

...