ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 싱글톤 클래스
    개발/C/C++ 2008.07.01 23:37

     

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

     

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

     

    싱글톤 클래스를 만드는 방법 중에 제가 자주(늘~) 사용하는 방법은 패턴 책에 구현된 코드가 아닌 “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점
    스콧 마이어스 지음, 곽용재 옮김/정보문화사


    댓글 5

    • 프로필사진

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

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

      2008.07.02 03:19
      • 프로필사진

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

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

        2008.07.02 08:07 신고
    • 프로필사진

      싱글턴 구현을 위한 템플릿 클래스를 만들어서 거기서 상속 받는 애들은 모두 싱글턴으로 접근할 수 있게 하기도 합니다. 구현 코드 카피 하면... (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

      2008.07.02 10:50
      • 프로필사진

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

        ATL 에서도

        DECLARE_CLASSFACTORY_SINGLETON

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

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

        2008.07.02 11:08 신고
      • 프로필사진

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

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

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

        2008.07.02 13:14 신고
Designed by black7375.