Когда / почему сделать функцию частной в классе? - PullRequest
19 голосов
/ 22 декабря 2010

Когда я должен сделать функцию private и почему это хорошая идея?

Ответы [ 7 ]

25 голосов
/ 22 декабря 2010

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

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

20 голосов
/ 22 декабря 2010

Я обычно делаю вспомогательные функции private. Но то, что помощник , кажется расплывчатым. Итак, позвольте мне привести вам пример. Предположим, у вас есть следующий класс Sample; он предоставляет мало публичных функций, одна из которых, скажем, DoWork(). Эта функция принимает один параметр. Но он не предполагает, что параметр всегда будет действительным, поэтому он first проверяет правильность параметра, для которого у него много кода в начале функции. Примерно так:

class Sample
{
   public:
      void DoWork(SomeClass param)
      {
               /*
                *lots of code related to validation of param
                */  

                //actual code that operates on the param 
                //and other member data IF the param is valid
      }
};

Поскольку вы написали много кода, связанного с проверкой параметра, это делает функцию громоздкой и трудной для чтения. Итак, вы решили перевести этот проверочный код в функцию, скажем, IsValidParam(), а затем вызываете эту функцию из DoWork(), передав ей параметр param. Примерно так:

class Sample
{
   public:
      void DoWork(SomeClass param)
      {       
            if ( IsValidParam(param))       
            {
                //actual code that operates on the param 
                //and other member data IF the param is valid
            }
      }
};

Это выглядит чище, верно?

Хорошо, вы написали IsValidParam() где-то в классе, но перед вами встает вопрос: вы бы сделали эту функцию public? Если эта функция используется только другими вашими функциями, такими как DoWork(), то сделать IsValidParam() public не имеет смысла. Итак, вы решили сделать эту функцию private.

class Sample
{
   public:
      void DoWork(SomeClass param)
      {       
            if ( IsValidParam(param))       
            {
                //actual code that operates on the param 
                //and other member data IF the param is valid
            }
      }
  private:
      bool IsValidParam(SomeClass param)
      {
          //check the validity of param.
          //return true if valid, else false.
      }
};

Функции такого рода (IsValidParam) должны быть private. Я называю эти функции вспомогательными функциями .

Надеюсь, это объяснение поможет вам!

5 голосов
/ 22 декабря 2010

Одним из основополагающих принципов ООП является инкапсуляция. Здесь функциональность того, как работает объект, хранится внутри этого объекта. Идея состоит в том, что для кода проще использовать объект, если ему не нужно знать, как он работает. Вроде как покупка микроволновки - нужно просто знать, как ею пользоваться, а не как она работает.

Тот же подход следует использовать с ООП. Держите все необходимое, чтобы объект был приватным. Сделайте только то, что нужно для полноценного использования объекта public.

2 голосов
/ 22 декабря 2010

Если вы разрабатываете класс - учитывая то, как его должен использовать клиентский код - тогда вы неизбежно получите интерфейс, состоящий из public и, возможно, protected членов.

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

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

1 голос
/ 22 декабря 2010

Насколько пуристом ты хочешь быть? :)

Правильный ответ на этот вопрос связан с поддержанием инвариантов. Правильный способ сделать это довольно сложно.

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

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

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

Частные вспомогательные функции также можно поместить в базу.

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

Частные функции обычно поддерживают очень слабые инварианты.

Причиной такой строгой иерархии является обеспечение правильного ведения инвариантов объектов.

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

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

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

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

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

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

0 голосов
/ 22 декабря 2010

private: используется только этим классом, не используется другими классами или производными классами.
protected: используется этим классом и, возможно, производными классами, но не используется другими классами.
public: используется другим классом, этот класс, и производный класс.

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

0 голосов
/ 22 декабря 2010

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

например: "ConnectionString ()".Каждое соединение с базой данных требует "ConnectionString ()", поэтому его объявлено Public.

...