Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Срауструп CPP.doc
Скачиваний:
4
Добавлен:
05.11.2018
Размер:
4.95 Mб
Скачать

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

В заключении можно обсудить, когда при обращении в закрытую часть

пользовательского типа стоит использовать функции-члены, а когда

функции-друзья. Некоторые функции, например конструкторы, деструкторы

и виртуальные функции ($$R.12), обязаны быть членами, но для других

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

не вводим нового глобального имени, при отсутствии других доводов

следует использовать функции-члены.

Рассмотрим простой класс X:

class X {

// ...

X(int);

int m1();

int m2() const;

friend int f1(X&);

friend int f2(const X&);

friend int f3(X);

};

Вначале укажем, что члены X::m1() и X::m2() можно вызывать только

для объектов класса X. Преобразование X(int) не будет применяться

к объекту, для которого вызваны X::m1() или X::m2():

void g()

{

1.m1(); // ошибка: X(1).m1() не используется

1.m2(); // ошибка: X(1).m2() не используется

}

Глобальная функция f1() имеет то же свойство ($$4.6.3), поскольку

ее параметр - ссылка без спецификации const. С функциями f2() и

f3() ситуация иная:

void h()

{

f1(1); // ошибка: f1(X(1)) не используется

f2(1); // нормально: f2(X(1));

f3(1); // нормально: f3(X(1));

}

Следовательно операция, изменяющая состояние объекта класса,

должна быть членом или глобальной функцией с параметром-ссылкой

без спецификации const. Операции над основными типами, которые

требуют в качестве операндов адреса (=, *, ++ и т.д.),

для пользовательских типов естественно определять как члены.

Обратно, если требуется неявное преобразование типа для всех

операндов некоторой операции, то реализующая ее функция должна

быть не членом, а глобальной функцией и иметь параметр типа ссылки

со спецификацией const или нессылочный параметр. Так обычно обстоит

дело с функциями, реализующими операции, которые для основных

типов не требуют адресов в качестве операндов (+, -, || и т.д.).

Если операции преобразования типа не определены, то нет

неопровержимых доводов в пользу функции-члена перед функцией-другом

с параметром-ссылкой и наоборот. Бывает, что программисту просто

одна форма записи вызова нравится больше, чем другая.

Например, многим для обозначения функции обращения матрицы m больше

нравится запись inv(m), чем m.inv(). Конечно, если функция

inv() обращает саму матрицу m, а не возвращает новую, обратную m,

матрицу, то inv() должна быть членом.

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

на функции-члене. Можно привести такие доводы. Нельзя гарантировать,

что когда-нибудь не будет определена операция обращения. Нельзя во

всех случаях гарантировать, что будущие изменения не повлекут за

собой изменения в состоянии объекта. Запись вызова функции-члена

ясно показывает программисту, что объект может быть изменен, тогда

как запись с параметром-ссылкой далеко не столь очевидна. Далее,

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

короче эквивалентных выражений в глобальной функции. Глобальная

функция должна использовать явно заданные параметры, а в

функции-члене можно неявно использовать указатель this. Наконец,

поскольку имена членов не являются глобальными именами, они обычно

оказываются короче, чем имен глобальных функций.