Эффективный C ++ Элемент 23 Предпочитайте функции, отличные от членов, не являющихся членами для функций-членов

Не смотря на то, что некоторые из них относятся к classу дизайна, в частности, должны ли они быть членами или нет, я посмотрел в «Эффективный c ++» и нашел пункт 23, а именно «Предпочесть не-членские функции, отличные от друга» для функций-членов. Чтение того, что из первых рук с примером веб-браузера имело какой-то смысл, однако удобные функции (названные такими нечеткими функциями, как это в книге) в этом примере изменяют состояние classа, не так ли?

  • Итак, первый вопрос, не должны ли они быть членами, чем?

  • Читая немного дальше, он считает, что функции STL и действительно некоторые функции, которые не реализованы некоторыми classами, реализованы в stl. Следуя идеям книги, они развиваются в некоторые удобные функции, которые упаковываются в некоторые разумные пространства имен, такие как std::sort , std::copy из algorithm . Например, vector class не имеет функции sort и используется функция sort stl, поэтому она не является членом векторного classа. Но можно было бы также разделить те же рассуждения на некоторые другие функции в векторном classе, такие как assign чтобы оно также не могло быть реализовано как член, а как функция удобства. Однако это также изменяет внутреннее состояние объекта, например, на котором он работал. Итак, в чем причина этого тонкого, но важного (я думаю) вопроса.

Если у вас есть доступ к книге, вы можете уточнить эти моменты немного больше для меня?

Доступ к книге вовсе не нужен.

Вопросы, которые мы имеем здесь, – это зависимость и повторное использование .

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

В хорошо спроектированном программном обеспечении вы применяете принцип DRY (Do not Repeat Yourself), потому что, когда требуется изменение, это болезненно и подвержено ошибкам, чтобы повторить его в десятках разных мест.

«Классический» образ мышления OO становится все более плохим при обработке зависимостей. Имея много и много методов, зависящих непосредственно от внутренних компонентов classа, малейшее изменение подразумевает полную переписывание. Это не должно быть так.

В C ++ STL (не вся стандартная библиотека) была разработана с явными целями:

  • зависания
  • использование повторного использования

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

Например, если вы думаете о требованиях алгоритма sort . Для реализации, используемой (в общем) STL, требуется (из контейнера):

  • эффективный доступ к элементу по заданному индексу: Random Access
  • возможность обмена двумя элементами: не ассоциативный

Таким образом, любой контейнер, который обеспечивает случайный доступ и не является ассоциативным, (теоретически) подходит для эффективного сортирования (скажем) алгоритма быстрой сортировки.

Каковы контейнеры на C ++, которые удовлетворяют этому?

  • базовый C-массив
  • deque
  • vector

И любой контейнер, который вы можете написать, если будете обращать внимание на эти детали.

Было бы расточительно, не так ли, переписывать (копировать / вставлять / настраивать) для каждого из них?

Обратите внимание, например, что существует метод std::list::sort . Зачем ? Поскольку std::list не предлагает произвольный доступ (неофициально myList[4] не работает), поэтому sort по алгоритму не подходит.

Критерии, которые я использую, – это если функция может быть реализована значительно более эффективно, будучи функцией-членом, тогда она должна быть функцией-членом. ::std::sort не соответствует этому определению. Фактически, нет никакой разницы в эффективности при реализации ее внешне и внутренне.

Огромное повышение эффективности, реализуя что-то в качестве функции члена (или друга), означает, что это очень полезно знать внутреннее состояние classа.

Часть искусства дизайна интерфейса – это искусство нахождения самого минимального набора функций-членов, так что все операции, которые вы, возможно, захотите выполнить на объекте, могут быть реализованы с достаточной эффективностью с точки зрения них. И этот набор не должен поддерживать операции, которые не должны выполняться в classе. Таким образом, вы не можете просто реализовать кучу функций getter и setter и назвать это хорошим.

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

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

Кстати, вы должны купить свою копию Effective C ++, это отличная книга, но не старайтесь всегда соблюдать каждый пункт этой книги. Объектно-ориентированный дизайн и передовой практики (из книг и т. Д.) И опыта (я думаю, что он также написан в Effective C ++ где-то).

Итак, первый вопрос, не должны ли они быть членами, чем?

Нет, это не следует. В идиоматическом дизайне classа C ++ (по крайней мере, в идиомах, используемых в Effective C ++ ), функции non-friend, не связанные друг с другом, расширяют интерфейс classа. Они могут считаться частью публичного API для classа, несмотря на то, что они не нужны и не имеют частного доступа к classу. Если этот проект является «не ООП» некоторым определением ООП, тогда, ОК, идиоматический С ++ не является ООП по этому определению.

растянуть те же рассуждения на некоторые другие функции в векторном classе

Это правда, есть некоторые функции-члены стандартных контейнеров, которые могли бы быть свободными функциями. Например, vector::push_back определяется в терминах insert и, безусловно, может быть реализован без личного доступа к classу. В этом случае push_back является частью абстрактного понятия, BackInsertionSequence , которое реализует вектор. Такие обобщенные концепции разрезают дизайн отдельных classов, поэтому, если вы разрабатываете или реализуете свои собственные общие концепции, которые могут влиять на то, где вы помещаете функции.

Конечно, есть части стандарта, которые, возможно, должны были быть разными, например, std :: string имеет слишком много функций-членов . Но все сделано, и эти classы были разработаны до того, как люди действительно обосновались в том, что мы теперь можем назвать современным стилем C ++. Класс работает в любом случае, поэтому есть только такая практическая выгода, которую вы можете получить от беспокойства по поводу этой разницы.

Мотивация проста: поддерживайте согласованный синтаксис. По мере того, как class развивается или используется, появляются различные функции удобства, отличные от членов; вы не хотите изменять интерфейс classа, чтобы добавить, например, что-то вроде toUpper в class строк. (В случае с std::string , конечно, вы не можете.) Скотт беспокоится, что когда это произойдет, вы получите несогласованный синтаксис:

 s.insert( "abc" ); toUpper( s ); 

Используя только свободные функции, объявляя их друзьями по мере необходимости, все функции имеют одинаковый синтаксис. Альтернативой было бы изменение определения classа каждый раз, когда вы добавляете функцию удобства.

Я не совсем уверен. Если class хорошо разработан, он имеет базовую функциональность, пользователю ясно, какие функции являются частью этой базовой функции и которые являются дополнительными функциями удобства (если таковые существуют). В глобальном масштабе строка – это особый случай, поскольку она предназначена для решения многих различных проблем; Я не могу представить, что это относится ко многим classам.

Различные мысли:

  • Приятно, когда не-члены работают через открытый API classа, поскольку он уменьшает количество кода, который:
    • необходимо тщательно контролировать, чтобы гарантировать инварианты classа,
    • необходимо изменить, если реализация объекта будет изменена.
  • Когда это не достаточно хорошо, не-член может по-прежнему быть friend .
  • Написание функции, не являющейся членом, обычно является менее удобным, поскольку члены не подразумеваются в области видимости, но если вы рассматриваете эволюцию программы:
    • Как только существует функция, не являющаяся членом, и становится понятным, что одна и та же функциональность будет полезна для других типов, обычно легко преобразовать эту функцию в шаблон и иметь ее доступную не только для обоих типов, но и для любых будущих типов. Иными словами, шаблоны, не являющиеся членами, позволяют использовать еще более гибкое повторное использование алгоритмов, чем polymorphism во время выполнения / виртуальная диспетчеризация: шаблоны позволяют что-то известное как утиная печать .
    • Существующий тип, поддерживающий полезную функцию-член, поощряет вырезание и вставку других типов, которые хотели бы аналогичного поведения, поскольку большинство способов преобразования функции для повторного использования требуют, чтобы каждый доступ неявного члена был предоставлен явный доступ к определенному объекту, который будет более тедием 30 секунд для программиста ….
  • Функции-члены допускают обозначение object.function(x, y, z) , которое IMHO очень удобно, выразительно и интуитивно. Они также лучше работают с функциями обнаружения / завершения во многих IDE.
  • Разделение в качестве функций-членов и нечленов может помочь сообщать о сущностной природе classа, его инвариантах и ​​фундаментальных операциях и логически группировать дополнительные и, возможно, специальные «удобства». Рассмотрите мудрость Тони Хоара:

    «Существует два способа построения программного обеспечения: один из способов – сделать его настолько простым, что, очевидно, нет недостатков, а наоборот – сделать его настолько сложным, что нет очевидных недостатков. Первый метод намного сложнее «.

    • Здесь использование не-членов не обязательно намного сложнее, но вам нужно больше думать о том, как вы обращаетесь к данным о членах, а также к частным / защищенным методам и почему и какие операции являются фундаментальными. Такой поиск души улучшил бы дизайн с помощью функций-членов, просто легче лениться о: – /.
  • Поскольку функциональность, отличная от членов, расширяется в изощренности или подбирает дополнительные зависимости, функции могут перемещаться в отдельные заголовки и файлы реализации, даже библиотеки, поэтому пользователи основной функциональности только «платят» за использование частей, которые они хотят.

(Ответ Omnifarious – обязательный, трижды, если он для вас новичок).

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

Interesting Posts

Как отобразить другое значение для выпадающего списка значений / выбранного элемента в WPF ComboBox?

Может ли панель предварительного просмотра Windows 7 Explorer не блокировать редактирование документов Acrobat и Word?

Разница между java.lang.RuntimeException и java.lang.Exception

Как проверить, является ли какое-либо значение NaN в Pandas DataFrame

Поглощает ли процессор / радиатор падение температуры?

Какова максимальная длина действительного адреса электронной почты?

Django получает QuerySet из массива id в определенном порядке

Heroku – обработка статических файлов в приложении Django

C-запятая

Каков эффект завершения пробела в строке формата scanf ()?

Создание новой строки в XSLT

.NET Формат строки с фиксированными пространствами

Как полностью «убить» фона рабочего?

Можно ли передать свойства как параметры «out» или «ref»?

Текст HTML с тегами для форматированного текста в ячейке Excel

Давайте будем гением компьютера.