Почему оператор Friend << используется в некоторых случаях, а в других - нет - PullRequest
2 голосов
/ 18 марта 2019

Скажите, у меня есть класс Complex.

Я видел оба

friend ostream& operator<<( ostream& stream, Complex z) (т.е. функция друга)

ostream& operator<<( ostream& stream, Complex z) (т.е. функция-член)

используется, и я изо всех сил пытаюсь понять, когда я должен исключить друга, а когда включение друга считается важным.

Мое понимание: и функция-член, и функция друга могут получать доступ к закрытым членам класса Complex, функция друга может быть определена только в самом классе, и она также имеет доступ к закрытым членам (и тоже общедоступным) другие классы, которых нет у функции-члена.

Дополнительный вопрос, поскольку ostream& operator<<( ostream& stream, Complex z) является функцией-членом, почему ее не нужно объявлять в самом классе? Я думал, что все функции-члены должны быть объявлены в классе?

Ответы [ 4 ]

3 голосов
/ 18 марта 2019

Вы можете реализовать перегрузку оператора обоими способами, и если вам не нужно вызывать ее с неявным преобразованием в тип Complex, вы можете использовать функцию-член. Но есть недостатки, и предоставление его через бесплатную функцию, friend, если необходим доступ к закрытым членам, стало стандартным способом.Лучшая ссылка здесь - статья Скотта Мейера о том, как свободные функции улучшают инкапсуляцию .Его совет относительно функции члена против не-члена явно включает operator <<:

if (f needs to be virtual)  
   make f a member function of C;  
else if (f is operator>> or  operator<<)  
{  
   make f a non-member function;  
   if (f needs access to non-public members of C)  
      make f a friend of C;  
}
else if (f needs type conversions on its left-most argument)  
{  
   make f a non-member function;  
   if (f needs access to non-public members of C)  
      make f a friend of C;  
}  
else if (f can be implemented via C's public interface)  
   make f a non-member function;  
else  
   make f a member function of C;  
2 голосов
/ 18 марта 2019

Перво-наперво, функции-друзья не определены внутри самого класса. Функции-друзья определяются как любая другая функция вне класса. Внутри класса делается только их объявление вместе с ключевым словом friend .

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

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

Аналогичным образом, когда вы определяете функцию перегрузки оператора как функцию-член, функция уже знает об объекте, который вызывает функцию. Теперь давайте рассмотрим ваш пример:

friend ostream& operator<<( ostream& stream, Complex z) 

Подумайте, как бы вы вызвали эту функцию:

Complex z;
cout << z;

Когда компилятор видит это выражение, он вызывает не являющуюся членом функцию operator << с вызовом </p>

operator<<(cout, z)

Если бы вы объявили эту функцию как:

ostream& operator<<( ostream& stream, Complex z) 

Это не функция-член. Функция-член будет выглядеть следующим образом:

ostream& operator<<(ostream& stream)

Почему ты спрашиваешь? Потому что функция уже знает все об объекте, вызывающем эту функцию. Поэтому нужно только знать о куте.

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

z << cout

Это приведет к вызову компилятора, например:

z.operator<<(cout)

Однако для таких операторов, как z << cout </strong>, не используется программист на C ++, и такие операторы могут привести только к путанице. Все ожидают, что cout << z </strong>, но так как вы перегрузили operator << в качестве функции-члена, вам придется сделать вызов, подобный <strong>z << cout </strong>. Поэтому для таких операторов предпочтительна функция не член. Также помните, что существуют определенные операторы, такие как (), [], -> или любой из операторов присваивания, которые могут быть перегружены только как функции-члены.

Надеюсь, вы понимаете, почему в некоторых случаях лучше использовать друга.

Относительно вашего второго вопроса:

Дополнительный вопрос, поскольку ostream & operator << (ostream & stream, Complex z) является функцией-членом, почему ее не нужно объявлять в самом классе? Я думал, что все функции-члены должны быть объявлены в классе? </p>

Все функции-члены должны быть объявлены внутри класса. Вы, вероятно, путаете декларацию и определение. Определение может быть внутри или снаружи класса, но объявление должно быть внутри класса. А также

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

Это неправильно. И дружественные функции, и нестатические функции-члены класса имеют доступ к закрытым и открытым переменным-членам этого класса. Только статическая функция-член не может получить доступ к нестатическим переменным-членам класса.

2 голосов
/ 18 марта 2019

Во-первых,

, поскольку ostream& operator<<( ostream& stream, Complex z) является функцией-членом, почему ее не нужно объявлять в самом классе?

потому что это не функция-член. Ни одна из функций не объявлена ​​с ключевым словом friend внутри класса.

функция друга может быть определена только в самом классе, и она также имеет доступ к закрытым членам (и тоже общедоступным) других классов, которые функция-член не

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

На исходный вопрос: вы используете friend, если вам это нужно, из-за того, что он делает. Ничего другого.

0 голосов
/ 18 марта 2019

1. Friend stream << означает, что оператор глобального потока может получить доступ к члену Complex. Для следующего примера в main () глобальный outStream может напрямую обращаться к члену Complex. </p>

int main(int ac, char** av)
{
    Complex complex;
    std::cout << complex;
    return 0;
}

2.Функция-член << комплекса означает, что сам комплекс является потоком, он может принимать параметр, тип которого является комплексным. Для следующего примера: </p>

int main(int ac, char** av)
{
    Complex comp1, comp2;
    comp1 << comp2;
    return 0;
}

Даже при том, что использование устарело, это грамматически правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...