Перво-наперво, функции-друзья не определены внутри самого класса. Функции-друзья определяются как любая другая функция вне класса. Внутри класса делается только их объявление вместе с ключевым словом 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>
Все функции-члены должны быть объявлены внутри класса. Вы, вероятно, путаете декларацию и определение. Определение может быть внутри или снаружи класса, но объявление должно быть внутри класса. А также
функция друга может быть определена только в самом классе, и она также имеет доступ к закрытым членам (и тоже общедоступным) других классов, чего нет у функции-члена.
Это неправильно. И дружественные функции, и нестатические функции-члены класса имеют доступ к закрытым и открытым переменным-членам этого класса. Только статическая функция-член не может получить доступ к нестатическим переменным-членам класса.