Ответы:
Некоторые причины, по которым вам может понадобиться приватный конструктор:
final
на уровень класса. По этой причине помещать приватный конструктор практически бесполезно.
final
. Это то, что Sun сделала для класса String и разумного фрагмента API, который не следует расширять.
Предоставляя приватный конструктор, вы предотвращаете создание экземпляров класса в любом месте, кроме этого самого класса. Существует несколько вариантов использования такого конструктора.
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.
friend
его там, где это не оправдано, и во многих случаях это плохая идея. К сожалению, сообщение было получено слишком хорошо, и многие разработчики никогда не изучают язык достаточно хорошо, чтобы понять, что случайное использование friend
не только приемлемо, но и предпочтительно . Ваш пример был именно таким. Умышленное сильное взаимодействие - это не преступление, это дизайнерское решение.
Все застряли на Синглтоне, вау.
Другие вещи:
Это может быть очень полезно для конструктора, который содержит общий код; частные конструкторы могут быть вызваны другими конструкторами, используя '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.
Вот некоторые из применений частного конструктора:
когда вы не хотите, чтобы пользователи создавали экземпляры этого класса или создавали класс, который наследует этот класс, как, например java.lang.math
, вся функция в этом пакете static
, все функции можно вызывать без создания экземпляра math
, поэтому конструктор объявляется как статический ,
Я видел вопрос от вас, касающийся той же проблемы.
Просто, если вы не хотите, чтобы другие могли создавать экземпляры, оставьте конструктор в ограниченной области. Практическое применение (пример) - шаблон синглтона.
Вы не должны делать конструктор приватным. Период. Сделайте его защищенным, чтобы вы могли расширить класс, если вам нужно.
Изменить: Я поддерживаю это, независимо от того, сколько отрицательных голосов вы бросаете на это. Вы отсекаете потенциал для будущего развития кода. Если другие пользователи или программисты действительно решили расширить класс, то они просто изменят конструктор на защищенный в исходном коде или байт-коде. Вы ничего не сделаете, кроме как сделать их жизнь немного сложнее. Включите предупреждение в комментарии вашего конструктора и оставьте это при этом.
Если это служебный класс, то более простое, правильное и элегантное решение - пометить весь класс как «статический финал», чтобы предотвратить расширение. Бесполезно просто пометить конструктор как приватный; действительно определенный пользователь всегда может использовать отражение для получения конструктора.
protected
конструктора является принудительное использование статических методов фабрики, которые позволяют ограничивать создание экземпляров или объединять и повторно использовать дорогие ресурсы (соединения с БД, собственные ресурсы).Конструктор является закрытым для некоторых целей, например, когда вам нужно реализовать синглтон или ограничить количество объектов в классе. Например, в реализации синглтона мы должны сделать конструктор приватным
#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 &);
};
Используйте компилятор, чтобы запретить пользователям использовать объект с недопустимыми конструкторами по умолчанию.
Цитируя 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 предоставляет общедоступный конструктор по умолчанию без параметров, если конструктор не предусмотрен, и если вы намерены предотвратить создание экземпляров, тогда нужен частный конструктор.
Нельзя пометить класс верхнего уровня как статический, и даже конечный класс может быть создан.
У служебных классов могут быть частные конструкторы. Пользователи классов не должны иметь возможность создавать экземпляры этих классов:
public final class UtilityClass {
private UtilityClass() {}
public static utilityMethod1() {
...
}
}
Одно из важных применений в классе 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
}
};
Это также подходит, если в вашем классе есть только статические методы. т.е. никому не нужно создавать экземпляр вашего класса
Это на самом деле одна очевидная причина: вы хотите построить объект, но не практично делать это (с точки зрения интерфейса) в конструкторе.
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
, ... и частный конструктор гарантирует , что пользователь правильно ограничен.
В дополнение к более известным применениям ...
Для реализации шаблона метода объекта , который я бы суммировал как:
«Частный конструктор, публичный статический метод»,
«Объект для реализации, функция для интерфейса».
Если вы хотите реализовать функцию с использованием объекта, и этот объект бесполезен вне выполнения одноразовых вычислений (с помощью вызова метода), то у вас есть 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, где он является последним этапом шаблона рефакторинга и заканчивается:
- Замените исходный метод на тот, который создает экземпляр нового класса, созданный с параметрами и получателем исходного метода, и вызывает «вычислить».
Это существенно отличается от других примеров: класс является инстанцируемым (в отличие от служебного класса), но экземпляры являются частными (в отличие от фабричных методов, включая синглтоны и т. Д.) И могут жить в стеке, поскольку они никогда не экранируются.
Этот шаблон очень полезен в ООП «снизу вверх», где объекты используются для упрощения низкоуровневой реализации, но не обязательно представлены внешне, и отличается от нисходящего ООП, который часто представлен и начинается с интерфейсов высокого уровня.
Иногда полезно, если вы хотите контролировать, как и когда (и сколько) создаются экземпляры объекта.
Среди прочего, используется в шаблонах:
Singleton pattern
Builder pattern
Использование частных конструкторов также может повысить читабельность / ремонтопригодность перед лицом доменного дизайна. Из «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 я думаю, что имеет смысл предотвратить непосредственное создание нового объекта.