Какой смысл делать конструктор закрытым в классе?

Почему мы должны сделать конструктор закрытым в классе? Как нам всегда нужно, чтобы конструктор был публичным.

24 ответа

Решение

Некоторые причины, по которым вам может понадобиться приватный конструктор:

  1. Доступ к конструктору возможен только из статического метода фабрики внутри самого класса. Синглтон также может принадлежать к этой категории.
  2. Служебный класс, который содержит только статические методы.

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

A. Ваши экземпляры класса созданы в static метод. static Затем метод объявляется как public,

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

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

Б. Ваш класс - одиночка. Это означает, что в программе существует не более одного экземпляра вашего класса.

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 члены. В этом случае экземпляр программы никогда не должен создаваться в программе.

Оставить "черный ход", который позволяет другому классу / функции друга создать объект способом, запрещенным для пользователя. В качестве примера можно привести контейнер, создающий итератор (C++):

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

Все застряли на Синглтоне, вау.

Другие вещи:

  • Не позволяйте людям создавать ваш класс в стеке; создавать частные конструкторы и возвращать указатели только через фабричный метод.
  • Предотвращение создания копий класса (конструктор личных копий)

Это может быть очень полезно для конструктора, который содержит общий код; частные конструкторы могут быть вызваны другими конструкторами, используя 'this(...);' нотации. Делая общий код инициализации в частном (или защищенном) конструкторе, вы также четко даете понять, что он вызывается только во время построения, что не так, если бы это был просто метод:

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

Это гарантирует, что вы (класс с закрытым конструктором) управляете тем, как вызывается конструктор.

Пример: метод статической фабрики в классе может возвращать объекты, так как метод фабрики выбирает их размещение (например, одноэлементная фабрика).

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

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

У нас также может быть приватный конструктор, чтобы описать создание объекта только определенным классом (по соображениям безопасности).

Один из способов сделать это - иметь класс друзей.

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.

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

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

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

Допустимое использование:

  • Одно хорошее использование protected Конструктор должен принудительно использовать статические фабричные методы, которые позволяют ограничивать создание экземпляров или объединять и повторно использовать дорогие ресурсы (соединения с БД, собственные ресурсы).
  • Синглтоны (обычно не очень хорошая практика, но иногда необходимо)

Когда вы не хотите, чтобы пользователи создавали экземпляры этого класса или создавали класс, который наследует этот класс, например java.lang.math, все функции в этом пакете staticвсе функции могут быть вызваны без создания экземпляра mathпоэтому конструктор объявляется как статический.

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

#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

}

Спасибо

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

class C
{
public:
    C(int x);

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

Используйте компилятор, чтобы запретить пользователям использовать объект с недопустимыми конструкторами по умолчанию.

Если это личное, то вы не можете его назвать ==> вы не можете создать экземпляр класса. Полезно в некоторых случаях, например, синглтон.

Здесь есть обсуждение и еще несколько примеров.

Я видел вопрос от вас, касающийся той же проблемы.

Просто если вы не хотите, чтобы другие могли создавать экземпляры, оставьте конструктор в ограниченном объеме. Практическое применение (пример) - шаблон синглтона.

Использование частных конструкторов также может повысить читабельность / ремонтопригодность перед лицом доменного дизайна. Из "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 я думаю, что имеет смысл предотвратить прямое создание нового объекта.

В дополнение к более известным применениям...

Для реализации шаблона метода объекта, который я бы суммировал как:

"Частный конструктор, публичный статический метод"
"Объект для реализации, функция для интерфейса"

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

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) { /* ... */ };
}

Этот шаблон объекта метода приведен в шаблонах Besttalk Best Practice, Kent Beck, стр. 34–37, где он является последним этапом шаблона рефакторинга и заканчивается:

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

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

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

Цитируя Effective Java, вы можете иметь класс с закрытым конструктором, чтобы иметь вспомогательный класс, который определяет константы (как статические конечные поля).

(РЕДАКТИРОВАТЬ: согласно комментарию это то, что может быть применимо только с Java, я не знаю, применима ли эта конструкция / необходима в других языках OO (скажем, C++))

Пример как ниже:

public class Constants {
    private Contants():

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

EDIT_1: опять же, приведенное ниже объяснение применимо к Java: (и ссылаясь на книгу, эффективная Java)

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

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

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

в отличие от кода, как

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

Также я думаю, что частный конструктор лучше передает замысел дизайнера класса Constants.

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

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

Иногда полезно, если вы хотите контролировать, как и когда (и сколько) экземпляров объекта создаются.

Среди прочего, используется в шаблонах:

Singleton pattern
Builder pattern

Это на самом деле одна очевидная причина: вы хотите построить объект, но не практично делать это (с точки зрения интерфейса) в конструкторе.

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,... и частный конструктор обеспечит правильное ограничение пользователя.

Одно из важных применений в классе 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
   }
};

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

Вы можете предотвратить создание экземпляра класса. См. Пример шаблона синглтона в качестве примера. Чтобы гарантировать уникальность, вы не можете позволить кому-либо создать его экземпляр:-)

У служебных классов могут быть частные конструкторы. Пользователи классов не должны иметь возможность создавать экземпляры этих классов:

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}

Если вы создаете частный конструктор, вам нужно создать объект внутри класса

      enter code here#include<iostream>
//factory method
using namespace std;
class Test
{
private:
Test(){
cout<<"Object created"<<endl;
}
public:
static Test* m1(){
    Test *t = new Test();
    return t;
}
void m2(){
    cout<<"m2-Test"<<endl;
  }
};
int main(){
Test *t = Test::m1();
   t->m2();
   return 0;
}
Другие вопросы по тегам