Когда / почему сделать функцию частной в классе?

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

8 ответов

Решение

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

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

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

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() публика не имеет смысла. Итак, вы решили сделать эту функцию 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, Я называю эти функции вспомогательными функциями.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Первая проблема заключается в том, что производный класс не имеет достаточного доступа к внутренним переменным или вспомогательным функциям для реализации улучшения. Т.е., без вспомогательных функций переписать выставленную функцию api было бы невозможно. Чтобы позаимствовать отличный ответ Наваза выше , поскольку я бы передал некоторый внешне определенный класс в качестве параметра в public-ly открытый API void DoWork(SomeClass param), и проверьте его с помощью вспомогательной функции bool IsValidParam(SomeClass param), если бы мне нужно было переписать, было бы больно без существующих вспомогательных функций, таких как IsValidParam, что можно сделать только как protected.

Вторая проблема заключается в том, что вам скорее нужно перезаписать внутреннюю функцию, чем повторно реализовать всю общедоступную вызывающую функцию. В этом случае вы можете объявить свои внутренние вспомогательные функции. См. Также Когда использовать частный виртуальный компьютер . Как и в приведенном выше примере, если я изменил SomeClass, или производная, или в другом контексте требуется другая (более строгая? более свободная?) проверка, я мог бы только переопределить virtual IsValidParam, и оставьте DoWorkфункционировать в одиночку. В общем, если вы можете унаследовать класс или использовать его в качестве основы, все равно лучше сделать его функциями .

В зависимости от природы программного обеспечения или дизайна архитектуры на ранних этапах может быть полезно объявить все функции public virtual или / тогда protected virtualпоскольку вы ограничиваете доступный api. Затем, наконец, вытащите virtual или объявить это privateкак требуется. Но, хотя и не ограничен, он упрощает последующие улучшения; особенно если это уменьшает необходимость редактирования уже «исправленных» или трудноизменяемых библиотек.

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

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

Другие вопросы по тегам