답변:
개인 생성자가 필요한 몇 가지 이유는 다음과 같습니다.
final
클래스 레벨을 설정해야합니다. 이러한 이유로 개인 생성자를 두는 것은 거의 쓸모가 없습니다.
final
입니다. 이것이 Sun이 String 클래스에 대해 한 일이며 확장해서는 안되는 합리적인 API 덩어리입니다.
개인 생성자를 제공하면이 클래스 이외의 다른 위치에서 클래스 인스턴스가 작성되지 않습니다. 이러한 생성자를 제공하기위한 몇 가지 사용 사례가 있습니다.
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
회원 만 포함한다는 의미 입니다. 이 경우 프로그램에서 개체 인스턴스를 만들면 안됩니다.
다른 친구 클래스 / 함수가 사용자에게 금지 된 방식으로 객체를 구성 할 수있게하는 "후문"을 떠나는 것. 염두에 두는 예는 반복자를 구성하는 컨테이너 (C ++)입니다.
Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
// and Container is a friend of Iterator.
friend
보증되지 않은 곳에서 달리 사용할 수있는 경험이 부족한 C ++ 프로그래머를 대상으로하는 것으로 보이며 나쁜 생각이있는 경우가 많이 있습니다. 안타깝게도이 메시지는 너무나 잘 받아 들여졌으며, 많은 개발자들은 가끔 사용하는 friend
것이 허용 될뿐만 아니라 바람직 하다는 것을 이해하기에 충분한 언어를 배우지 않습니다 . 당신의 예는 바로 그런 경우였습니다. 고의적 인 강한 결합은 범죄가 아니라 디자인 결정입니다.
이를 통해 사용자 (개인 생성자가있는 클래스)가 생성자 호출 방식을 제어 할 수 있습니다.
예제 : 팩토리 메소드가 객체를 할당하기로 선택한 경우 (예 : 싱글 톤 팩토리와 같이) 클래스의 정적 팩토리 메소드가 객체를 반환 할 수 있습니다.
또한 특정 클래스에 의한 객체 생성을 보장하기 위해 개인 생성자를 가질 수도 있습니다 (보안상의 이유로).
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.
}
};
참고 : ¬¸ × Ì : SecureClass의 친구이므로 ClientClass 만 SecureClass의 생성자를 호출 할 수 있습니다.
private 생성자의 사용법은 다음과 같습니다.
동일한 문제를 해결하는 질문이 있습니다.
다른 사람이 인스턴스를 생성하지 못하게하려면 컨스트럭터를 제한된 범위 내로 유지하십시오. 실제 응용 (예)은 싱글 톤 패턴입니다.
당신은 안 생성자는 비공개. 기간. 필요한 경우 수업을 확장 할 수 있도록 보호하십시오.
편집 : 나는 당신이 이것에 던지는 공무원 수에 관계없이 그것을 기다리고 있습니다. 코드에서 향후 개발 가능성을 차단하고 있습니다. 다른 사용자 또는 프로그래머가 실제로 클래스를 확장하기로 결정한 경우 생성자를 소스 또는 바이트 코드로 보호하도록 변경합니다. 당신은 그들의 삶을 조금 더 어렵게 만드는 것 외에는 아무것도 성취하지 못할 것입니다. 생성자의 주석에 경고를 포함하고 그 상태로 두십시오.
유틸리티 클래스 인 경우 더 간단하고 정확하며 우아한 솔루션은 전체 클래스를 "정적 최종"으로 표시하여 확장을 방지하는 것입니다. 생성자를 비공개로 표시하는 것은 좋지 않습니다. 실제로 결정된 사용자는 항상 리플렉션을 사용하여 생성자를 얻을 수 있습니다.
protected
생성자를 잘 사용하는 한 가지 방법은 정적 팩토리 메소드를 강제로 사용하여 인스턴스화 또는 풀을 제한하고 고가의 리소스 (DB 연결, 기본 리소스)를 재사용 할 수 있습니다.생성자는 싱글 톤을 구현하거나 클래스의 객체 수를 제한해야 할 때와 같은 목적으로 비공개입니다. 예를 들어 싱글 톤 구현에서는 생성자를 비공개로 만들어야합니다
#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 : 다시는, 아래 설명을 자바에 적용 할 수있다 : (그리고 책을 참조 효과적인 자바 )
아래 클래스와 같은 유틸리티 클래스의 인스턴스화는 유해하지는 않지만 인스턴스화되도록 설계되지 않았기 때문에 어떤 목적으로도 사용되지 않습니다.
예를 들어, 상수 상수에 대한 개인 생성자가 없다고 가정하십시오. 아래와 같은 코드 청크는 유효하지만 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
, ..., 전용 생성자는 사용자가 적절하게 제한되어 있는지 확인합니다.
더 잘 알려진 용도 외에…
Method Object 패턴 을 구현하려면 다음과 같이 요약합니다.
“개인 생성자, 공개 정적 메서드”
“구현 용 객체, 인터페이스 기능”
객체를 사용하여 함수를 구현하고 일회성 계산 (메소드 호출에 의한) 이외의 객체가 유용하지 않은 경우 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) { /* ... */ };
}
이 메소드 객체 패턴은 34-37 페이지의 켄트 벡 (Kent Beck) Smalltalk 모범 사례 패턴에 나와 있으며 , 리팩토링 패턴의 마지막 단계 인 다음과 같습니다.
- 원래 메소드를 새 클래스의 인스턴스를 작성하고 원래 메소드의 매개 변수 및 수신자로 구성하고 "계산"을 호출하는 메소드로 바꾸십시오.
이것은 클래스의 인스턴스화 가능 (유틸리티 클래스와 달리)이지만 인스턴스는 프라이빗 (단일 톤 등의 팩토리 메소드와는 달리)이며, 절대 탈출하지 않기 때문에 스택에 살 수 있습니다.
이 패턴은 객체가 저수준 구현을 단순화하는 데 사용되지만 외부에 노출 될 필요는 없으며 상향식 OOP와 대조되는 상향식 OOP에서 매우 유용합니다.
객체의 인스턴스가 언제 어떻게 (그리고 얼마나 많은) 생성되는지를 제어하려는 경우에 유용합니다.
무엇보다도 패턴에 사용됩니다.
Singleton pattern
Builder pattern
개인 생성자를 사용하면 도메인 중심 디자인의 측면에서 가독성 / 유지 관리 성이 향상 될 수 있습니다. "Microsoft .NET-엔터프라이즈 용 엔터프라이즈 애플리케이션, 2 판":
var request = new OrderRequest(1234);
"여기에 두 가지 문제가 있습니다. 먼저, 코드를 볼 때 어떤 일이 일어나고 있는지 거의 추측 할 수 없습니다. OrderRequest의 인스턴스가 작성되고 있지만 왜 어떤 데이터를 사용합니까? 1234는 무엇입니까? 이것은 두 번째 문제로 이어집니다. 고객이 주문 요청을 발행하고 구매 ID를 지정할 수있는 경우 다음과 같이 말할 수 있습니다. : "
var request = OrderRequest.CreateForCustomer(1234);
어디
private OrderRequest() { ... }
public OrderRequest CreateForCustomer (int customerId)
{
var request = new OrderRequest();
...
return request;
}
나는 모든 단일 클래스에 대해 이것을 옹호하지는 않지만 위의 DDD 시나리오에서는 새로운 객체를 직접 생성하는 것을 막는 것이 합리적이라고 생각합니다.