싱글톤 클래스

2008.07.01 23:37개발/C/C++

 

어플리케이션을 개발하다 보면 어플리케이션 전역으로 쓰이는 필수 클래스가 있게 마련입니다. 이런 클래스들은 그 특성상 하나의 인스턴스만 생성되어야 하고, 시스템 전역에서 사용하다 보니 이 클래스의 코드 수정으로 인한 시스템에 걸쳐 있는 다른 프로그램들의 수정이 최소화되도록 설계되는 게 보통입니다.

 

하나의 인스턴스만 생성되어야 하는 클래스를 보통 싱글톤 클래스라고 부르는데요, 요즘은 패턴관련 책들이 워낙 많아 싱글톤 패턴들은 이미 머리 속에 콕 하고 들어 있을 겁니다.

 

싱글톤 클래스를 만드는 방법 중에 제가 자주(늘~) 사용하는 방법은 패턴 책에 구현된 코드가 아닌 “More Effective C++” 책에 소개된 방법입니다.


class Printer

{

public:

           void SubmitPrint();      

           ..

           friend Printer& thePrinter();

private:

           Printer();

           ...

};

 

Printer& thePrinter()

{

           static Printer p;

           return p;

}

 

// 호출

thePrinter().SubmitPrint();



책에서 소개된 방법은 짧고 아주 강력합니다.

1.     Printer 클래스는 생성자가 private으로 선언되어 있습니다. 하나의 인스턴스만 만들어야 하기 때문에 아무나 이 클래스를 만들지 못하게 하기 위해 이와 같은 강력한 제한을 두는 거지요.

2.     Factory 로 쓰이는 thePrinter() 함수는 Printer friend 로 되어 있습니다. 즉 이 함수는 언제든 Printer 의 모든 멤버 함수에 접근할 길을 터 놓았습니다.

3.     thePrinter () 구현 함수에서 Printer static 으로 선언했습니다. 정적 객체로 만들어진 변수의 장점은 잘 아시다시피

A.     하나의 인스턴스만 생성된다는 것입니다.

B.     함수 thePrinter() 가 호출되기 전에는 Printer 가 만들어 질 일이 없습니다. 최소한 한번이라도 호출되어야 생성된다고 보장하는 셈입니다. 바로 이 점이 Printer 클래스를 단순히 전역으로 선언하는 것보다 훨씬 나은 방법이지요.

 

뭐 이 정도만으로도 아주 훌륭한 싱글톤 클래스라고 생각합니다. 여기서 조금 더 나아간다면


class Printer


의 헤더와 구현 부분을 실제 이 클래스를 사용하는 클라이언트와 완전히 분리하기 위해 interface IPrinter 와 같은 virtual 로만 이뤄진 인터페이스만 제공하는 방법이 있습니다. 이는 당연히 Printer 클래스의 수정으로부터 클라이언트를 보호해서 대규모 개발에 적합한 설계로 나아가기 위함입니다.  더 설명하지 않아도 잘 아실 테니 요건 Skip ^^;

 

 

사실 “More Effective C++” 책에는 이외에도 참 좋은 테크닉들이 많이 담겨 있습니다. 위 싱글톤 클래스는 단지 간단한 활용 예일 뿐이고요. 더 좋은 예들은 책을 구입해서 확인하시기 바랍니다.





More Effective C++ - 10점
스콧 마이어스 지음, 곽용재 옮김/정보문화사


  • 프로필사진
    BlogIcon object2008.07.02 03:19

    재밌는 테크닉이네요. Private 생성자인데 프렌드 함수의 내부에서 생성한다라..

    저는 그냥 MFC의 CWinApp 스타일을 따릅니다. MFC 프로그램이라면 CWinApp 상속 클래스에 다 밀어 넣죠. 그리고 어디서나 theApp로 접근합니다. theApp는 보통 .h 파일에 extern으로 선언되어있어서 어디서나 가져갈 수 있죠 (멀티스레딩 상황은 좀 거시기해집니다만). 비슷하게 싱글톤 클래스를 만들면 그냥 클래스에 다 밀어넣고 역시 매번 인크루드 되는 파일에 extern으로 선언을 하고, 코드에서는 바로 그 변수로 접근하는 방식을 즐겨 씁니다.

    • 프로필사진
      BlogIcon esstory2008.07.02 08:07 신고

      저희도 전역으로 WinApp 등에 글로벌로 사용했었는데요 . 프로젝트가 커져서 dll 가 100 개가 넘는데 모두 winapp 에 그러한 공통 클래스가 있으니까, 문제가 많아지더라구요. 수백개의 dll 이 전역 클래스가 변경되어두 전혀 재 빌드가 없어야 하는 상황등이 있어서.. 위 방법에 interface 묶는게 현재로서는 가장 완벽한 방법으로 여기고 있습니다.

      게다가 위 방법은, 함수내 static 이라 함수가 호출될때까지는 인스턴스가 만들어지지 않는다는 좋은 장점도 있어요^^

  • 프로필사진
    BlogIcon 오스카2008.07.02 10:50

    싱글턴 구현을 위한 템플릿 클래스를 만들어서 거기서 상속 받는 애들은 모두 싱글턴으로 접근할 수 있게 하기도 합니다. 구현 코드 카피 하면... (GPG 1권, 중간에 offset 관련된 코드는 다중상속 문제 해결을 위한 것)

    template <typename T> class CSingleton
    {
    static T* ms_singleton ;

    public:
    CSingleton (void)
    {
    assert (!ms_singleton) ;
    int offset = (int)(T*)1 - (int)(CSingleton <T>*)(T*)1 ;
    ms_singleton = (T*)((int)this + offset) ;
    }
    ~CSingleton (void)
    {
    assert (ms_singleton) ; ms_singleton = 0 ;
    }
    static T& GetSingleton (void)
    {
    assert (ms_singleton) ;
    return (*ms_singleton) ;
    }
    static T* GetSingletonPtr (void)
    {
    return (ms_singleton) ;
    }
    } ;

    template <typename T> T* CSingleton <T>::ms_singleton = 0 ;

    class cExample : public CSingletion<cExample>
    {
    }

    #define Example cExample::GetSingleton()

    Example.AnyMethod(); /// blablabla

    • 프로필사진
      BlogIcon esstory2008.07.02 11:08 신고

      언뜻 봐서는 잘 이해가 안가네요.. 역시 템플릿은 어려워요 ㅎㅎ.

      ATL 에서도

      DECLARE_CLASSFACTORY_SINGLETON

      한줄 추가하면 해당 클래스가 바로 싱글톤이 되는 방식과 유사한가 봅니다.

      좋은 코드 감사합니다. 역시 강호는 넓어요~

    • 프로필사진
      BlogIcon esstory2008.07.02 13:14 신고

      혹시요.
      http://www.npteam.net/497

      과 비슷한 템플릿 같은데요.

      템플릿 생성자에 new T 같은게 있어야 하지 않나요?.

1 2 3 4 5 6 7 8 9 10