51

내 친구가 왜 세계 전역의 정적 객체보다 싱글 톤의 사용을 선호해야 하는가? 내가 설명하기 시작한 방법은 싱글 톤이 상태 대 정적 전역 객체를 가질 수 없다는 것이 었습니다.하지만 확실하지 않았습니다. 왜냐하면 C ++의이 때문에 .. (C #에서 왔습니다)

어떤 이점이 다른 것보다 무엇입니까? (C ++에서)

8 답변


56

사실, C ++에서 선호하는 방법은 로컬 정적 객체입니다.

Printer & thePrinter() {
    static Printer printer;
    return printer;
}

이것은 기술적으로는 싱글 톤이지만,이 함수는 클래스의 정적 메서드 일 수도 있습니다. 따라서 전역 정적 객체와 달리 사용하기 전에 생성 된 순서대로 생성 될 수 있으므로 하나의 전역 객체가 다른 객체를 사용할 때 일관성없이 실패 할 수 있습니다. 매우 일반적인 시나리오입니다.

호출을 통해 새로운 인스턴스를 생성하여 싱글 톤을 수행하는 일반적인 방법보다 나은 점은 무엇입니까?new그 객체 소멸자는 프로그램의 끝에서 호출 될 것입니다. 동적으로 할당 된 싱글 톤에서는 발생하지 않습니다.

또 하나의 긍정적 인 측면은 싱글 톤이 생성되기 전에 싱글 톤에 접근 할 수있는 방법이 없다는 것입니다. 다른 정적 메소드 나 서브 클래스에서도 가능합니다. 디버깅 시간을 절약 해줍니다.


  • 이것은 싱글 톤을 만드는 가장 좋은 방법입니다. 새로운 코드를 사용하는 것은 실제 코드에서는 흔한 일이 아닙니다. 예를 들어 인터넷에 좋은 아이디어 인 것처럼 보이지만 (믿지 않아요) 많은 예제가 있습니다. - Martin York
  • 실제로, "최상의" 싱글 톤을 만드는 방법. 이 싱글 톤이 소멸자의 다른 싱글 톤을 참조하면 제대로 작동하지 않습니다. - rlbond
  • 일반적으로 이것은 thread-safe가 아닙니다 (구현에 따라 다릅니다. 예 : g ++는 잠금을 삽입하지만 MSVC는 잠금을 해제합니다). - Pavel Minaev
  • 만약 당신의 싱글 톤이 다른 싱글 톤을 참조한다면, 다른 코드 라인을 쓰는 것을 금지해야합니다. 그리고 더 많은 싱글 톤을 쓰는 것이 좋습니다. 그런 다음 모든 데이터가 글로벌 여야한다는 1970 년대 정신에 머물러 있습니다. - jalf
  • ... 그리고 나서 당신은 그렇게 할 수 있습니다.Printer다른 사람이 생성자를 비공개로 만들고 인스턴스를 만들면 인스턴스화되지 않습니다.thePrinter()친구 기능. - Catskul

22

C ++에서 서로 다른 컴파일 단위로 정적 객체를 인스턴스화하는 순서는 정의되지 않았습니다. 따라서 하나의 글로벌이 생성되지 않은 다른 글로벌을 참조하여 프로그램을 폭파 할 수 있습니다. 싱글 톤 패턴은 생성자를 정적 멤버 함수 또는 자유 함수에 묶음으로써이 문제를 제거합니다.

알맞은 요약이 있습니다.이리.


  • 두 스레드가 동시에 해당 함수를 호출 할 수 있기 때문에 복잡한 경쟁 조건을 도입하고 스레드 안전을 버립니다. - jalf
  • Funnily enough, std :: cout은 평범하고 오래된 전역 객체 임에도 불구하고 잘 동작 할 것입니다 ... 초기화의 순서가 그렇게 큰 문제 였다면 표준위원회는 지금까지 주목했을 것입니다. 글로벌을 과도하게 사용하고 서로를 의존하게하는 것은 단지 문제입니다. - jalf
  • @ jalf : 숙제해라.coutcin전에 특별히 건설되도록 지정되어있다.main()표준에 의해. 다른 전역 개체는이 치료를받지 못합니다. 또한 스레드 안전 싱글 톤 구현이 존재하며 자신을 작성하는 것이 어렵지 않습니다. 이중 구조를 방지하기 위해 이중 잠금 장치 만 있으면됩니다. 개인적으로 나는 싱글 톤을 피하려고 노력한다. 패턴이 과용된다고 생각한다. 그러나 당신의 C ++ 지식의 부족은 나를 downvote 할 이유가 없습니다. - rlbond
  • 이중 잠금 패턴은아니이 경우에 일하십시오. 정적 객체는 초기화 될 것이 보장됩니다.동일한 번역 단위의 함수가 호출되기 전에이것은 객체를 초기화하려는 객체를 포함합니다. 즉, 객체가 잠기기 전에 발생하기 때문에 사용하지 않을 수 있습니다. :) - jalf
  • @rlbond : cin과 cout에 특별한 것은 없습니다. 그들은 다른 전역 변수와 똑같이 취급됩니다. 그들이 정확하게 초기화되었는지 확인하는 데 사용할 수있는 또 다른 트릭이 있습니다. - Martin York

8

내 친구가 왜 세계 전역의 정적 객체보다 싱글 톤의 사용을 선호해야 하는가? 내가 설명하기 시작한 방법은 싱글 톤이 상태 대 정적 전역 객체를 가질 수 없다는 것이 었습니다.하지만 확실하지 않았습니다. 왜냐하면 C ++의이 때문에 .. (C #에서 왔습니다)

정적 전역 객체는 C #에서도 상태를 가질 수 있습니다.

class myclass {
 // can have state
 // ...
 public static myclass m = new myclass(); // globally accessible static instance, which can have state
}

어떤 이점이 다른 것보다 무엇입니까? (C ++에서)

싱글 톤은 코드를 손상시키고 전역 정적 인스턴스는 그렇지 않습니다. 그래서 이미 싱글 톤 문제에 관해 수많은 질문이 있습니다.여기 하나있어.,그리고 또 다른,또는 다른.

즉, 싱글 톤은 두 가지를 제공합니다.

  • 전역 적으로 액세스 가능한 객체 및
  • 하나의 인스턴스 만 생성 될 수 있음을 보장합니다.

첫 번째 요점 만 원하면 전역 적으로 액세스 할 수있는 객체를 만들어야합니다. 과우리는 둘째를 원할 것입니까? 우리는하지 않는다.알고있다앞으로 우리 코드가 어떻게 사용될 수 있는지 미리 알아 두어야합니다. 그렇다면 유용한 기능을 제거하는 이유는 무엇입니까? 우리는 보통잘못된우리는 "나는 단지 하나의 인스턴스 만 필요합니다"라고 예측할 때. 그리고 "나는 단지 하나의 인스턴스 만 필요합니다"(정확한 답은 다음과 같습니다.몹시 떠들어 대다하나의 인스턴스), "하나 이상의 인스턴스가 생성되면 응용 프로그램이 제대로 작동하지 않을 수 있습니다. 사용자의 하드 드라이브를 포맷하고 중요한 데이터를 인터넷에 게시 할 것입니다"(대답은 다음과 같습니다. 앱이 고장 났지만만약그것은 그렇지 않습니다, 그렇습니다, 싱글 톤은 여러분이 필요로하는 것입니다)


  • 싱글 톤 패턴에는 몇 가지 합법적 인 용도가 있습니다. 너무 많이 사용되었지만 괜찮은 경우도 있습니다 (몇 가지 예 : 게임의 전역 RNG, 구성 옵션 객체, 메모리 풀 컨트롤러). - rlbond
  • 그리고 위에서 언급 한 것처럼 다른 컴파일 단위로 객체를 만드는 순서는 정의되지 않았습니다. 너는 나를 믿지 않고 자랐다.coutcin, 이것들은 표준에 의해 정의 된 특별한 경우이다. - rlbond
  • 게임에서 하나의 글로벌 RNG를 사용해야하는 이유는 거의 없습니다. 경합을 줄이고 스레드 당 하나씩 가져 오는 것은 어떻습니까? 그리고 서브 시스템 당 다른 시스템을 원할 수도 있습니다. 또는 멀티 플레이어 시나리오에서 플레이어별로. 구성 옵션? 나는 2 인용으로 쉽게 사용할 수있다. 활성화 된 하나의 구성 집합과 사용자가 현재 수정 중이지만 아직 적용되지 않은 두 번째 집합이 있습니다. 당신은 할 수있다용이하게둘 이상의 메모리 풀을 가지고있다. 물론, 거기아르싱글 톤 패턴에 대한 합법적 인 사용. 하지만 그들은 매우 드물고 나열한 것들은아니그들 사이에. - jalf
  • 사실, 내가 작성한 이후로 타협이 덜 해졌을 수도 있지만 위의 의견을 수정하고아니싱글 톤 패턴에 대한 합법적 인 사용. - jalf
  • 내 코드가 앞으로 어떻게 사용될 것인지 미리 알고 있습니다. 지금 가능한 다른 용도로 잠그고 있습니다. 미래의 변화는 중요한 토론을 요구할 것입니다. 그래서, 그 경우, 싱글턴은 괜찮습니까? - iheanyi

3

이유 1 :

싱글 톤은 쉽게 만들 수 있으므로 게으르다.

전역 적으로이 작업을 수행 할 수 있지만 개발자가 추가 작업을 수행합니다. 따라서 기본적으로 전역 변수는 항상 초기화됩니다 (네임 스페이스가있는 일부 특수 규칙 제외).

따라서 객체가 크거나 비싸면 실제로 사용하지 않으면 빌드하지 않을 수도 있습니다.

이유 2 :

초기화 (및 파기) 문제의 순서.

GlobalRes& getGlobalRes()
{
    static GlobalRes instance;  // Lazily initialized.
    return instance;
}


GlobalResTwo& getGlobalResTwo()
{
    static GlobalResTwo instance;  // Lazy again.
    return instance;
}


// Order of destruction problem.
// The destructor of this object uses another global object so
// the order of destruction is important.
class GlobalResTwo
{
    public:
        GlobalResTwo()
        {
            getGlobalRes();
            // At this point globalRes is fully initialized.
            // Because it is fully initialized before this object it will be destroyed
            // after this object is destroyed (Guaranteed)
        }
        ~GlobalResTwo()
        {
            // It is safe to use globalRes because we know it will not be destroyed
            // before this object.
            getGlobalRes().doStuff();
        }
};


3

전역 정적 객체에 대한 Singleton의 또 다른 이점은 생성자가 private이기 때문에 "하나만있을 수 있습니다"라는 매우 명확한 컴파일러 강제 지시문이 있다는 것입니다.

비교해 보면 전역 정적 객체의 경우이 객체의 추가 인스턴스를 만드는 개발자가 코드를 작성하는 것을 멈추게하는 요소는 없습니다.

추가 제한 조건의 이점은 오브젝트가 어떻게 사용될 것인지에 대한 보증을한다는 것입니다.


  • 컴파일러가 실시한 지시문은 "하나만 존재할 수있다"는 " 이것이 유익한 단일 유스 케이스를 생각해 내십시오.좋은의회? 코드에 임의의 불필요한 제약 조건을 추가하는 것은 이점이 아닙니다. 코드가 다음과 같은 경우 이점이 있습니다.일반재사용 가능한가장 먼저하는 일은 필요하지 않은 제약 조건을 때리는 것입니다. - jalf
  • @ jalf : 대부분의 코드는 일반적이고 재사용 가능해야합니다. 그러나 대규모 응용 프로그램에서는 전역 응용 프로그램 프레임 워크 인 일부 중앙 참조가 필요한 여러 개체를 가질 수 있습니다. 예를 들어 어떤 종류의 잠금 관리자가 있습니다. 자체 잠금 관리자를 만드는 응용 프로그램의 다른 부분은 부적절합니다. - Andrew Shepherd
  • 아니요, 여러 구성 요소에 대해 서로 다른 잠금 관리자를 쉽게 가질 수 있습니다. - jalf
  • @ jalf : 상황에 따라 다릅니다. 그러나 더 큰 그림을 보면 : 당신이 반대하는 것처럼 보이는 것은 클래스의 디자이너가 클래스 사용 방법에 제약을 가하는 것입니다. 저는 이것이 컴포넌트 디자인의 필수적인 부분이라고 주장 할 것입니다. 이것이 메소드와 필드를 비공개로 정의 할 수있는 이유입니다. - Andrew Shepherd

2

Singleton ( "처음 사용할 때 생성") 관용구를 사용하면정적 초기화 순서 실패


  • 그 기사는 끔찍합니다. 제안 된 해결책은 문제만큼 나쁘다. 함수에서 new를 사용하면 절대로 소멸자를 호출하지 않습니다. - Martin York
  • 이것은 메모리 누출이 괜찮은 아주 드문 경우 중 하나입니다. 프로그램이 어쨌든 끝나기 때문에 (메모리를 회수하지 않는 참으로 끔찍한 OS를 사용하지 않는 한). 다음 FAQ를 읽으면 왜 그 이유를 설명합니다. - coppro
  • 10.12 절 ~ 10.16 절을 읽으십시오. - aJ.
  • 메모리 관리 관점에서 보면 괜찮습니까?하지만 소멸자가 추가 작업을해야한다면 어떻게 될까요? - Michael Foukarakis
  • 나는 최근에 추가 작업을하기 위해 싱글 톤 소멸자가 필요한 문제를 가지고있었습니다. 나는 스마트 포인터 (boost :: shared_ptr)를 사용하여 인스턴스가 잘 작동한다는 것을 알았다. 10.14와 관련된 문제가 발생할 수 있습니다. - Dan Hook

0

C ++에서는 실제 유용성면에서이 둘 사이에 엄청난 차이가 없습니다. 전역 객체는 당연히 고유 한 상태를 유지할 수 있습니다 (다른 전역 변수와도 연관 될 수 있지만, 권장하지는 않습니다). 글로벌 또는 싱글 톤을 사용하려는 경우 (그리고 그렇게하지 않을 많은 이유가 있습니다) 글로벌 객체에 싱글 톤을 사용하는 가장 큰 이유는 싱글 톤을 사용하면 여러 클래스를 상속 받아 동적 다형성을 가질 수 있다는 것입니다 싱글 톤 기본 클래스


  • 이 다형성은 어떻게 구현되고 유스 케이스는 무엇입니까? - underscore_d

-1

좋아요, 정말로 싱글 톤으로가는 두 가지 이유가 있습니다. 하나는 모두가 말하는 정적 순서입니다.

다른 하나는 코드를 사용할 때 누군가가 다음과 같이하지 못하게하는 것입니다.

CoolThing blah;
gs_coolGlobalStaticThing = blah;

또는 더 나쁜 경우 :

gs_coolGlobalStaticThing = {};

캡슐화 측면은 바보와 악의적 인 멍청이로부터 인스턴스를 보호합니다.


  • 접근 자에 대해 이야기하고 있습니다 ... - Flavien Volken

연결된 질문


관련된 질문

최근 질문