Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 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. Наконец,       поскольку имена членов не являются глобальными именами, они обычно       оказываются короче, чем имен глобальных функций.