Когда, в 1991 г., я писал первое издание "Эффективное использование C++" (Effective C++ [1]), я изучал проблему определения функций, связанных с классом. Для заданного класса C и функции f, связанной с C, я разработал следующий алгоритм:
if (f необходимо быть виртуальной) сделайте f функцией-членом C;
else
if (f – это operator›› или operator‹‹) {
сделайте f функцией – не членом;
if (f необходим доступ к непубличным членам C) сделайте f другом C;
}
else if (в f надо преобразовывать тип его крайнего левого аргумента) {
сделайте f функцией – не членом;
if (f необходимо иметь доступ к непубличным членам C) сделайте f другом C;
} else сделайте f функцией-членом C;
Этот алгоритм хорошо служил мне многие годы, и когда я правил "Эффективное использование C++" для второй издания в 1997 г. [2], я не сделал никаких изменений в этот алгоритм.
Однако, в 1998 году, когда я проводил презентацию в Actel, где Аран Канду (Arun Kundu) заметил, что мой алгоритм диктовал, что функции должны быть методами даже тогда, когда они могли бы быть реализованы как не члены, которые использовали только открытый интерфейс класса C. "Это действительно, что-нибудь означает?" – спросил он меня? Другими словами, если f могла бы быть реализован как функция-член (метод) или как функция не являющаяся не другом, не членом, я действительно защищал, что ее надо реализовать как метод класса? Я немного подумал об этом и решил, что это не то, то, что я подразумевал. Поэтому я изменил алгоритм [3]:
if (f необходимо быть виртуальной) сделайте f функцией-членом C;
else if (f – это operator›› или operator‹‹) {
сделайте f функцией – не членом;
if (f необходим доступ к непубличным членам C) сделайте f другом C;
} else if (f необходимо преобразовывать тип его крайнего левого аргумента) {
сделайте f функцией – не членом;
if (f необходимо иметь доступ к непубличным членам C) сделайте f другом C;
} else if (f может быть реализована через доступный интерфейс класса) сделайте f функцией – не членом;
else сделайте f функцией-членом C;
С той поры я критиковал программистов, которые освоили урок, преподанный объектно-ориентированным подходом, предлагающий помещать функции внутрь классов, содержащих данные, обрабатываемые этими функциями. В ответ они возражают мне, что это все делается ради инкапсуляции.
Но они ошибаются.