У чому полягає використання приватного конструктора в класі?


134

Чому ми повинні робити конструктор приватним у класі? Як нам завжди потрібен конструктор, щоб бути публічним.

Відповіді:


129

Деякі причини, коли вам може знадобитися приватний конструктор:

  1. Доступ до конструктора можна отримати лише із статичного заводського методу всередині самого класу. Сінглтон також може належати до цієї категорії.
  2. Клас корисності , який містить лише статичні методи.

9
Я б навіть не потурбувався створити приватний конструктор для класу корисності.
Петеш

16
@Patesh: Це ваше рішення. Інші (і) люди (і) я скоріше запобігатимуть встановленню класу корисності, ніж залишати один рядовий приватний конструктор.
nanda

1
у деяких мовах програмування (зокрема Java) це також запобігає успадкуванню
dfa

9
@dfa: щоб запобігти успадкуванню, потрібно поставити його finalна рівні класу. Поставляти приватний конструктор з цієї причини майже марно.
nanda

3
@Will: Ні, якщо ви використовуєте рефлексію. Оголошення конструкторів приватними призначене для запобігання інстанції (відбиття заборони), але запобігання субкласифікації є побічним ефектом, а не наміром. Відповідним інструментом для цього є оголошення класу final. Це те, що Sun зробив для класу String, і розумний фрагмент API, який не слід розширювати.
BobMcGee

96

Надаючи приватний конструктор, ви запобігаєте створенню екземплярів класу в будь-якому місці, крім цього самого класу. Існує кілька випадків використання для надання такого конструктора.

A. Ваші екземпляри класу створені staticметодом. Потім staticметод оголошується як public.

class MyClass()
{
private:
  MyClass() { }

public:
  static MyClass * CreateInstance() { return new MyClass(); }
};

B. Ваш клас - одинокий . Це означає, що в програмі існує не більше одного примірника вашого класу.

class MyClass()
{
private:
  MyClass() { }

public:
  MyClass & Instance()
  {
    static MyClass * aGlobalInst = new MyClass();
    return *aGlobalInst;
  }
};

C. (стосується лише майбутнього стандарту C ++ 0x) У вас є кілька конструкторів. Деякі з них задекларовані public, інші private. Для зменшення розміру коду, державні конструктори "викликають" приватних конструкторів, які, в свою чергу, виконують всю роботу. publicТаким чином, ваші конструктори називаються конструкторами, що делегують :

class MyClass
{
public:
  MyClass() : MyClass(2010, 1, 1) { }

private:
  MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};

D. Ви хочете обмежити копіювання об'єктів (наприклад, через використання спільного ресурсу):

class MyClass
{
  SharedResource * myResource;

private:
  MyClass(const MyClass & theOriginal) { }
};

E. Ваш клас - корисний клас . Це означає, що він містить лише staticчленів. У цьому випадку жоден примірник об'єкта ніколи не повинен створюватися в програмі.


3
У більшості випадків ви хочете не допустити копіювання об'єктів. Таким чином, ви б не забезпечили реалізацію для приватного конструктора копій.
фраст

з посібника зі стилів google c ++ (ще один приклад не в списку, але загальний) -> якщо вам потрібно виконати роботу в конструкторі "врахуйте заводську функцію [і зробивши конструктор приватним]" (текст у квадратних дужках пишеться мною )
Тревор Бойд Сміт

12

Залишити "задню двері", що дозволяє іншому другому класу / функції будувати об'єкт забороненим для користувача способом. Прикладом, який спадає на думку, може бути контейнер, що створює ітератор (C ++):

Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
//     and Container is a friend of Iterator.

Це досить різний, Террі? ;)
Еміль Корм'є

Хороша відповідь. Чому б не згадати рефлексію, оскільки це одна дорога для досягнення чогось, що користувач не може?
BobMcGee

@Bob: Вам доведеться просвітити мене на цьому, оскільки я в основному хлопець C ++, і рефлексія насправді не в словнику C ++. ;)
Еміль Корм'є,

3
Ніхто не збирається цього читати, але ось: я пару разів звертався до цього. Не засмучений чи що-небудь, але (заради навчання) я хотів би знати, чому це погано? Як ще можна побудувати ітератор з даними, необхідними для доступу до даних контейнера?
Еміль Корм'є

2
@EmileCormier, я думаю, ви несправедливо визнали, що люди, які вивчають C ++, знову і знову говорять: "Уникайте заяв про друзів". Ця рада, схоже, спрямована на недосвідчених програмістів на C ++, які в іншому випадку можуть використовувати friendтам, де це не гарантовано, - і є маса випадків, коли це погана ідея. На жаль, повідомлення було занадто добре сприйняте, і багато розробників ніколи не вивчають мову достатньо добре, щоб зрозуміти, що епізодичне використання friendне тільки прийнятне, але і бажане . Ваш приклад був саме таким випадком. Навмисна сильна зв'язок - це не злочин, це дизайнерське рішення.
ухилення від потоку

10

Усі застрягли на річ Синглтон, ух.

Інші речі:

  • Не дозволяйте людям створювати ваш клас на стеку; робити приватні конструктори і лише повертати покажчики назад фабричним методом.
  • Запобігання створенню копій класу (приватний конструктор копій)

8

Це може бути дуже корисно для конструктора, який містить загальний код; приватні конструктори можуть викликати інші конструктори, використовуючи 'this (...);' позначення. Створюючи загальний код ініціалізації в приватному (або захищеному) конструкторі, ви також чітко даєте зрозуміти, що він викликається лише під час побудови, що не так, якби це був просто метод:

public class Point {
   public Point() {
     this(0,0); // call common constructor
   }
   private Point(int x,int y) {
     m_x = x; m_y = y;
   }
};

3

Є деякі випадки, коли ви, можливо, не хочете використовувати громадський конструктор; наприклад, якщо ви хочете однокласний клас.

Якщо ви пишете збірку, використовувану третьою стороною, може бути ряд внутрішніх класів, які ви хочете створити лише вашою асамблеєю, а не користуватися ними.


3

Це гарантує, що ви (клас із приватним конструктором) контролюєте, як викликається кондуктор.

Приклад: статичний заводський метод класу може повертати об'єкти, оскільки заводський метод вирішує виділити їх (наприклад, завод однотонних).


@Skilldrick: одним із прикладів може бути те, що ви можете змусити клас виділяти лише купу.
Дірк

@dirk Я видалив свій коментар, оскільки він більше не стосується.
Skilldrick

3

Ми також можемо мати приватний конструктор, щоб передбачити створення об'єкта лише певним класом (з міркувань безпеки).

Один із способів зробити це - через клас друзів.

Приклад C ++:

class ClientClass;
class SecureClass 
{
  private:
    SecureClass();   // Constructor is private.
    friend class ClientClass;  // All methods in 
                               //ClientClass have access to private
                               // &   protected methods of SecureClass.
};

class ClientClass
{
public:
    ClientClass();
    SecureClass* CreateSecureClass()
     { 
           return (new SecureClass());  // we can access 
                                        // constructor of 
                                        // SecureClass as 
                                        // ClientClass is friend 
                                        // of SecureClass.
     }
};

Примітка. Примітка. Тільки ClientClass (оскільки він є товаришем SecureClass) може викликати Конструктор SecureClass.


2

2

коли ви не хочете, щоб користувачі створювали екземпляри цього класу або створювали клас, який успадковує цей клас, як, наприклад java.lang.math, вся функція в цьому пакеті static, всі функції можна викликати без створення екземпляра math, тому конструктор оголошується як статичний .


1

Якщо він приватний, ви не можете його назвати ==> Ви не можете створити інстанцію клас. Корисний у деяких випадках, як сингл.

Там в обговоренні і ще кілька прикладів тут .


1

Я побачив від вас запитання, що стосується тієї ж проблеми.

Просто якщо ви не хочете дозволити іншим створювати екземпляри, тоді тримайте кондуктор в обмеженому обсязі. Практичне застосування (Приклад) - це однотонний малюнок.


1

Не слід робити конструктор приватним. Період. Зробіть його захищеним, щоб ви могли розширити клас, якщо потрібно.

Редагувати: Я стою на цьому, незалежно від того, скільки голосів ви кинете на це. Ви скорочуєте потенціал для подальшого розвитку коду. Якщо інші користувачі або програмісти дійсно налаштовані на розширення класу, вони просто змінять конструктор на захищений у вихідному чи байт-коді. Ви нічого не зробите, крім того, щоб зробити їх життя трохи важчим. Включіть попередження до коментарів вашого конструктора та залиште це при цьому.

Якщо це корисний клас, більш простим, правильним та елегантнішим рішенням є позначення всього класу "статичним фіналом" для запобігання розширення. Немає користі лише позначити конструктор приватним; дійсно визначений користувач завжди може використовувати відображення для отримання конструктора.

Дійсне використання:

  • Одне хороше використання protected конструктора - змусити використовувати статичні заводські методи, які дозволяють обмежити інстанціювання або об'єднати та повторно використовувати дорогі ресурси (з'єднання БД, рідні ресурси).
  • Одномісні (зазвичай це не найкраща практика, але іноді необхідні)

2
На мій досвід, немає абсолютних істин, навіть гото може бути використане, якщо цього вимагає ситуація. Є поганий і злий способи, але, як зазначає Маршалл Клайн, іноді потрібно вибирати між меншими злом. parashift.com/c++-faq-lite/big-picture.html#faq-6.15 Що стосується приватних конструкторів, вони потрібні і навіть не погані для вас. Це просто означає, що ніхто, включаючи ваші підкласи, не повинен ним користуватися.
daramarak

Готос має своє місце, правда, але маркування приватного конструктора не приносить вам нічого, крім неприємностей у дорозі. Я відредагував свій пост, щоб більш повно пояснити, чому.
BobMcGee

Немає сенсу копіювати конструкцію або конструювати за замовчуванням деякі класи. У C ++ ви вказуєте це, оголошуючи приватний ctor, не визначаючи його (що також звичайно для оператора =).

Оптимізація компіляторів, таких як Java JIT і GWT, насправді використовує приватні конструктори: обмежити обсяг інстанції та зробити кращу роботу з вбудовування / обрізки вашого коду.
Аякс

1

Конструктор приватний для певних цілей, наприклад, коли вам потрібно реалізувати одиночний або обмежити кількість об'єктів класу. Наприклад, в одиночній реалізації ми повинні зробити конструктор приватним

#include<iostream>
using namespace std;
class singletonClass
{


    static int i;
    static singletonClass* instance;
public:


    static singletonClass* createInstance()
    {


        if(i==0)
        {

            instance =new singletonClass;
            i=1;

        }

        return instance;

    }
    void test()
    {

        cout<<"successfully created instance";
    }
};

int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{


    singletonClass *temp=singletonClass::createInstance();//////return instance!!!
    temp->test();
}

Знову ж таки, якщо ви хочете обмежити створення об’єкта до 10, то скористайтеся наступним

#include<iostream>
using namespace std;
class singletonClass
{


    static int i;
    static singletonClass* instance;
public:


    static singletonClass* createInstance()
    {


        if(i<10)
        {

            instance =new singletonClass;
            i++;
            cout<<"created";

        }

        return instance;

    }
};

int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{


    singletonClass *temp=singletonClass::createInstance();//return an instance
    singletonClass *temp1=singletonClass::createInstance();///return another instance

}

Дякую


1

У вас може бути більше одного конструктора. C ++ надає конструктор за замовчуванням і конструктор копій за замовчуванням, якщо ви не надаєте його явно. Припустимо, у вас є клас, який можна побудувати лише за допомогою деякого параметризованого конструктора. Можливо, це ініціалізовані змінні. Якщо користувач використовує цей клас без цього конструктора, вони можуть не викликати проблем. Хороше загальне правило: Якщо реалізація за замовчуванням не є дійсною, зробіть як конструктор за замовчуванням, так і конструктор копій приватним і не надайте реалізацію:

class C
{
public:
    C(int x);

private:
    C();
    C(const C &);
};

Використовуйте компілятор, щоб запобігти користувачам використовувати об'єкт із конструкторами за замовчуванням, які не є дійсними.


2
Якщо ви надаєте конструктор, який не використовується за замовчуванням, не вказуючи конструктор за замовчуванням, конструктор за замовчуванням не існує. Не потрібно його створювати і робити приватним.
TT_

0

Цитуючи з Ефективної Java , ви можете мати клас з приватним конструктором, щоб мати клас утиліти, який визначає константи (як статичні кінцеві поля).

( EDIT: Відповідно до коментаря, це щось, що може бути застосоване лише для Java, я не знаю, чи ця конструкція застосовна / потрібна в інших мовах ОО (скажімо, C ++))

Приклад, як показано нижче:

public class Constants {
    private Contants():

    public static final int ADDRESS_UNIT = 32;
    ...
}

EDIT_1 : Знову нижче, пояснення застосовується в Java: (і посилається на книгу " Ефективна Java" )

Опис класу корисності, як наведений нижче, хоч і не є шкідливим, але не виконує жодних цілей, оскільки вони не розроблені для примірника.

Наприклад, скажіть, що немає приватного конструктора для констант класу. Код, як показано нижче, є дійсним, але не краще передає наміри користувача класу Константи

unit = (this.length)/new Constants().ADDRESS_UNIT;

на відміну від коду типу

unit = (this.length)/Constants.ADDRESS_UNIT;

Також я думаю, що приватний конструктор краще передає наміри конструктора класу Константи (скажімо).

Java надає загальнодоступний конструктор без параметрів за замовчуванням, якщо не передбачений конструктор, і якщо ваш намір полягає у запобіганні інстанції, тоді потрібен приватний конструктор.

Не можна відзначити статику класу вищого рівня, і навіть остаточний клас можна створити екземпляром.


2
Але в C ++ вам не потрібен клас для цього. Якщо щось не залежить від даних всередині об'єкта, запишіть його як вільні функції та вільні змінні. Хочете капсулювання? Використовуйте простір імен.
daramarak

@daramarak: дякую, я не маю досвіду роботи з C ++. Я оновив відповідь, щоб відобразити, що це застосовується лише в Яві
sateesh

2
Якщо ваш об'єкт просто інкапсулює статичні змінні, навіщо обмежувати інстанціювання? Навіщо вказувати щось про конструктор? Це не принесе ніякої шкоди, якщо воно буде примірником. Здається, ви могли б отримати подібні результати, оголосивши клас статичним і остаточним.
BobMcGee

@BobMcGee, дякую за ваш коментар Це змусило мене більше подумати про те, що я розмістив. Я відредагував свою відповідь, щоб додати ще кілька міркувань про корисність приватного конструктора.
sateesh

0

Корисні класи можуть мати приватних конструкторів. Користувачі класів не повинні мати можливість ініціювати ці класи:

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}

1
Чому це важливо, якщо клас корисності примірник? Все, що вона робить, це створити об’єкт без полів і з'їсти кілька байт пам'яті.
BobMcGee

Можливість інстанціювати це створює неоднозначний API. Якщо ви проектуєте клас як клас корисності без стану і хочете зберегти його таким чином. Ви можете підкласирувати клас із загальнодоступним конструктором. Підклас деяких корисних методів є ідіотичним.
gpampara

@BobMcGee: очевидно, проектування класу, який працює, і проектування класу, який використовуються іншими людьми, - це різні речі. Ті, хто працює в розробці API (наприклад, користувачі Sun або хлопець колекції Google), вб'ють усіх, хто намагається сказати, що приватний конструктор класу корисності марний.
nanda

@gpampara: Оголошення вашого класу остаточним не дозволяє людям підкласифікувати. Це те, що Sun зробив для класу String. @Nanda: Клас утиліти не потребує визначеного конструктора. Якщо ви не хочете, щоб він був розширюваним, тоді оголосити його не слід, використовуючи "остаточний".
BobMcGee

1
@BobMcGee: Ви, здається, любите бачити приклад від Sun. Потім перевірте цей колекцій утиліти від Sun: docjar.com/html/api/java/util/Collections.java.html . Дійсно використовується приватний конструктор.
nanda

0

Ви можете попередити вільне інстанціювання класу. Див. Приклад дизайну однотонної форми. Щоб гарантувати унікальність, ви не можете дозволити нікому створювати його примірник :-)


0

Одне з важливих напрямків використання в класі SingleTon

class Person
{
   private Person()
   {
      //Its private, Hense cannot be Instantiated
   }

   public static Person GetInstance()
   {
       //return new instance of Person
       // In here I will be able to access private constructor
   }
};

Він також підходить, якщо у вашому класі є лише статичні методи. тобто нікому не потрібно призначати ваш клас


Слід зазначити, що одинарне вважається багатьма "анти-закономірністю", і рішення про його застосування не слід приймати легко. Поширеною альтернативою є ін'єкційна залежність.
Майкл Аарон Сафян

SingleTon не є антидією. однак, надмірне використання шаблону (через недостатнє знання про його використання) не є корисним. Слід зазначити, що це один із схем ГОФ.
SysAdmin

0

Це дійсно одна очевидна причина: ви хочете створити об’єкт, але це не практично (в інтерфейсі) робити це в конструкторі.

FactoryПриклад абсолютно очевидно, дозвольте мені продемонструвати Named Constructorідіоми.

Скажіть, у мене клас, Complexякий може представляти складне число.

class Complex { public: Complex(double,double); .... };

Питання полягає в тому: чи очікує конструктор реальні та уявні частини, чи очікує норму та кут (полярні координати)?

Я можу змінити інтерфейс, щоб полегшити його:

class Complex
{
public:
  static Complex Regular(double, double = 0.0f);
  static Complex Polar(double, double = 0.0f);
private:
  Complex(double, double);
}; // class Complex

Це називається Named Constructorідіомою: клас можна побудувати лише з нуля, чітко вказавши, який конструктор ми хочемо використовувати.

Це особливий випадок багатьох методів будівництва. Шаблони дизайну забезпечують велику кількість способів об'єкта збірки: Builder, Factory, Abstract Factory, ... і приватний конструктор гарантує , що користувач правильно обмежений.


0

Окрім більш відомих цілей використання…

Для реалізації шаблону " Об'єкт методу" , який я б узагальнив як:

"Приватний конструктор, публічний статичний метод"
"Об'єкт для реалізації, функція для інтерфейсу"

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

z = new A(x,y).call();

… Замінивши його на виклик функції (у просторі імен):

z = A.f(x,y);

Користувачеві ніколи не потрібно знати або піклуватися про те, що ви використовуєте об'єкт внутрішньо, створюючи більш чистий інтерфейс і не допускаючи сміття з предмета, що висить навколо, або неправильного використання об'єкта.

Наприклад, якщо ви хочете розбити обчислення по методах foo , barі zork, наприклад, поділити стан, не маючи передавати багато значень у функції та виходити з них, ви можете реалізувати його наступним чином:

class A {
  public static Z f(x, y) {
    A a = new A(x, y);
    a.foo();
    a.bar();
    return a.zork();
  }

  private A(X x, Y y) { /* ... */ };
}

Цей метод "Об'єктний шаблон" наведений у " Шаблонах найкращих практик" Smalltalk , Кент Бек, сторінки 34–37, де це останній крок шаблону рефакторингу, який закінчується:

  1. Замініть оригінальний метод тим, який створює екземпляр нового класу, побудований з параметрами та приймачем вихідного методу, і викликає "обчислити".

Це суттєво відрізняється від інших тут прикладів: клас є миттєвим (на відміну від класу утиліти), але екземпляри є приватними (на відміну від заводських методів, включаючи одиночні кнопки тощо), і можуть жити на стеці, оскільки вони ніколи не виходять.

Ця закономірність дуже корисна в OOP знизу-вгору, де об’єкти використовуються для спрощення реалізації низького рівня, але не обов'язково піддаються зовнішньому виконанню і контрастують з OOP зверху вниз, який часто представлений і починається з інтерфейсів високого рівня.


0

Іноді корисно, якщо ви хочете контролювати, як і коли створюються (і скільки) екземпляри об’єкта.

Серед інших, що використовуються в візерунках:

Singleton pattern
Builder pattern

Чим корисний приватний конструктор у незмінному об’єкті? Незмінність полягає у запобіганні змін об’єкту після створення , тоді як приватні конструктори - про запобігання створенню .
Нілс фон Барт

0

Використання приватних конструкторів також може означати підвищення читабельності / ремонтопридатності в умовах дизайну, керованого доменом. З "Microsoft .NET - програми архітектури для підприємства, 2-е видання":

var request = new OrderRequest(1234);

Цитата: "Тут є дві проблеми. По-перше, дивлячись на код, навряд чи можна здогадатися, що відбувається. Створюється екземпляр OrderRequest, але чому і за допомогою яких даних? Що таке 1234? Це призводить до другої проблеми: ви порушуєте всюдисущу мову обмеженого контексту. Мова, ймовірно, говорить приблизно так: клієнт може надіслати запит на замовлення і йому вдається вказати ідентифікатор покупки. Якщо це так, ось кращий спосіб отримати новий екземпляр OrderRequest : "

var request = OrderRequest.CreateForCustomer(1234);

де

private OrderRequest() { ... }

public OrderRequest CreateForCustomer (int customerId)
{
    var request = new OrderRequest();
    ...
    return request;
}

Я не виступаю за це для кожного класу, але для вищезазначеного сценарію DDD я думаю, що має сенс запобігти прямому створенню нового об’єкта.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.