Друзья или члены

Друзья класса

Пример с ошибками

Примеры

void print(int);

void print(const char *);

void print(double);

void print(long);

void print(char);

char c; int i; short s; float f;

print(c); // правило 1; вызывается print(char)

print(i); // правило 1; вызывается print(int)

print(s); // правило 2; вызывается print(int)

print(f); // правило 2; вызывается print(double)

print('a'); // правило 1; вызывается print(char)

print(49); // правило 1; вызывается print(int)

print("a"); // правило 1; вызывается print(const char *)

void f(int, float);

void f(float, int);

Вызов, который приведет к генерации сообщения об ошибке (неоднозначный выбор):

f(1.5, 1.5);

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

Функция становится другом после ее объявления в классе с использованием спецификатора friend, например:

Определение Реализация
а) глобальная функция class X{ ... friend void f(); public: void fx(); ... } void f() { ... }
б) функция – член класса class Y{ ... friend void X::fx(); ... }; void X::fx() { ... }
в) класс class Z{ ... friend class Y; ... };  

Другом класса могут быть: глобальная функция (см. примеры, приведенные выше); функция-член другого класса; все функции-члены другого класса, т.е. сам класс (при этом функции-друзья одного класса не становятся автоматически друзьями другого класса). Почти любой метод может быть сделан другом. Исключения: конструкторы, деструктор, кое-что еще.

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

Различия между членами и друзьями класса:

Функция-член Функция-друг
class Rational{ public: void print(); ... }; void Rational::print() { cout << num; if(den!= 1) cout << '/' << den; } ... Rational x(1,5); x.print(); class Rational{ public: friend void print(Rational r); ... }; void print(Rational r) { cout << r.num; if(r.den!= 1) cout << '/' << r.den; } ... Rational x(1,5); print(x);

Перегруженные операторы – друзья класса:

Бинарный оператор Унарный оператор (префиксный и постфиксный)
Объявление
friend тип operator знак_оп (op1, op2) friend тип operator знак_оп (op1) friend тип operator знак_оп (op1, int)
Реализация
тип operator знак_оп (тип op1, тип op2) {... } тип operator знак_оп (тип op1) {... } тип operator знак_оп (тип op1, int) {... }
Использование
op1 знак_оп op2 эквивалентно: operator знак_оп (op1, op2) знак_оп op1 op1 знак_оп

Пример перегрузки оператора записи в поток для класса Rational:

class Rational {

friend ostream& operator <<(ostream&, Rational);

...

};

ostream& operator <<(ostream& os, Rational r)

{

os << r.num;

if(r.den == 1)

os << ‘/’ << r.den;

return os;

}

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

Общее:

- имеют доступ к закрытой части класса,

- хотя бы один аргумент – экземпляр класса

Различие:

член друг
- из n параметров один (первый) параметр неявный, остальные – в списке параметров - все n параметров в списке параметров
- неявный параметр – адресат сообщения; доступен через this - все параметры равноправны; адресата сообщения нет; this не определено
- адресат сообщения (первый аргумент) – обязательно экземпляр класса - порядок и типы аргументов определяются объявлением функции
Rational x(1,3); x + 1 - все в порядке 1 + x - ошибка! Rational x(1,3); x + 1 - все в порядке 1 + x - все в порядке

Функции-члены класса:

- конструкторы, деструкторы, виртуальные функции;

- операции, требующие в качестве операндов основных типов lvalue (например, =, +=, ++ и т.д.)

- операции, изменяющие состояние объекта

Функции-друзья класса:

- операции, требующие неявного преобразования операндов (например, +, - и т.д.)

- операции, первый операнд которых не соответствует типу экземпляра класса (например, << и >>).

При прочих равных условиях лучше выбирать функции-члены класса.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: