Не являющийся членом недруг функционирует по сравнению с закрытыми функциями

Есть только одна причина, по которой я использую intellij, а не eclipse: Удобство использования

Будь то отладка, рефакторинг, автозаполнение. Intellij гораздо проще использовать с последовательными привязками клавиш. опции доступны там, где вы их ищите и т. д. Что касается функций, то для intellij будет сложно догнать Eclipse, так как последний имеет гораздо больше доступных плагинов, чем intellij, и его легко расширить.

14
задан Jared 30 June 2009 в 00:45
поделиться

4 ответа

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

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

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

class Number { // small simple interface: accessor to constant data, constructor
public:
  explicit Number( int nNumber ) : m_nNumber( nNumber ) {}
  int value() const { return m_nNumber; }
private:
  int m_nNumber;
};
Number operator+( Number const & lhs, Number const & rhs ) // Add addition to the interface
{
   return Number( lhs.value() + rhs.value() );
}
Number operator-( Number const & lhs, Number const & rhs ) // Add subtraction to the interface
{
   return Number( lhs.value() - rhs.value() );
}

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

Сложная часть (не в приведенном выше упрощенном примере) - это определение наименьшего интерфейса, который вы должны предоставить. Статья ( GotW # 84 ), на которую ссылается предыдущий вопрос, является отличным примером. Если вы прочтете его подробно, то обнаружите, что можете значительно сократить количество методов в std :: basic_string, сохранив при этом ту же функциональность и производительность. Подсчет снизится с 103 до 32 членов. Это означает, что изменения реализации в классе затронут только 32 члена вместо 103, и поскольку интерфейс сохраняется, 71 свободная функция, которая может реализовать остальную функциональность с точки зрения 32 членов, не должна быть изменена.

Это важный момент: он более инкапсулирован, поскольку вы ограничиваете влияние изменений реализации на код.

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

class ReallyComplex
{
public:
   ReallyComplex& operator+=( ReallyComplex const & rhs );
};
ReallyComplex operator+( ReallyComplex const & lhs, ReallyComplex const & rhs )
{
   ReallyComplex tmp( lhs );
   tmp += rhs;
   return tmp;
}

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

Приведенный выше код является общим шаблоном, хотя обычно вместо получения операнда lhs по постоянной ссылке и создания внутри временного объекта его можно изменить так, чтобы параметр сам был копией значения, что помогает компилятор с некоторыми оптимизациями:

ReallyComplex operator+( ReallyComplex lhs, ReallyComplex const & rhs )
{
   lhs += rhs;
   return lhs;
}
17
ответ дан 1 December 2019 в 09:02
поделиться

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

Я настоятельно рекомендую прочитать Effective C ++ от Скотта Мейерса , в котором объясняется, почему вы должны это делать и когда это нужно.

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

11
ответ дан 1 December 2019 в 09:02
поделиться

Похоже, что этот вопрос уже был задан при ответе на другой вопрос.

Такие правила, как правило, хороши для слуг и плохих хозяев, есть обмен . Будет ли результат более устойчивым, если применить предложенное преобразование? Зачем? Я считаю, что предполагаемое преимущество состоит в том, что, уменьшив количество методов, напрямую работающих с личными данными объекта, вы сможете легче понять его поведение и, следовательно, упростить его обслуживание.

Я не верю, что ваш последующий пример достигает этой цели. [Вы можете заметить, что приведенный выше пример «после» все равно не компилируется, но это уже другая история. ] Если мы настроим его для реализации внешних функций исключительно в терминах общедоступных методов, а не внутреннего состояния (добавим аксессор для значения), тогда, я думаю, мы получили некоторый выигрыш. Достаточно, чтобы гарантировать работу. Мое мнение: нет. Я думаю, что преимущества предлагаемого перехода к внешней функции становятся намного больше, когда методы обновляют значения данных в объекте. Я хотел бы реализовать небольшое количество мутаторов, поддерживающих неизменяемые, и реализовать основные "бизнес-методы" в терминах этих - экстернализация этих методов - это способ гарантировать, что они могут работать только в терминах мутаторов.

2
ответ дан 1 December 2019 в 09:02
поделиться

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

Я также часто использую идиому pImpl. На мой взгляд, оба подхода имеют следующие преимущества:

  • Чистый дизайн интерфейса

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

  • Отделение от реализации

    ] Если вы измените что-то в файле .cpp без изменения интерфейса, вам нужно будет только перекомпилировать один файл .cpp (модуль компиляции) и повторно связать приложение, что значительно ускорит время сборки.

  • Скрыть зависимости от другие классы, которые используют ваш интерфейс

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

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

0
ответ дан 1 December 2019 в 09:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: