이 영역을 누르면 첫 페이지로 이동
웬디의 기묘한 이야기 블로그의 첫 페이지로 이동

웬디의 기묘한 이야기

페이지 맨 위로 올라가기

웬디의 기묘한 이야기

C/C++ Windows Hooking 개발자의 블로그 입니다! 이곳은 개발 외에도 저의 취미들이 공유되는 기묘한 이야기가 펼쳐집니다.

[C++] 싱글톤 패턴 - Singleton Pattern

  • 2015.12.17 18:09
  • ⌨ DEVELOPMENT/Design Pattern
반응형

Singleton Pattern


GOF의 23가지 패턴 중 가장 쉬우면서 많이쓰이며, 가장 문제가 될 소지를 가지는 패턴입니다.

먼저 Singleton Pattern의 용도는 하나의 프로그램 내에서 하나의 인스턴스만을 생성해야 하는 상황에서 사용됩니다.

공용 데이터를 관리하는 클래스나, 환경설정등을 관리하는 클래스의 경우엔 하나의 인스턴트로 관리되는 것이 일반적이며, 이때 Singleton Pattern을 적용할 수 있습니다.


class singleton {
private:
    static singleton* _instance;

    singleton() {}
    singleton(const singleton& other);
    ~singleton() {}
public:
    static singleton* instance() {
        if (_instance == nullptr) {
            _instance = new singleton();
        }
        return _instance;
    }
};


실제 사용은 간단합니다. 직접적으로 생성을 할 수 없으며,

Singleton::instance()->function(); 와 같이 instance를 통해서만 접근하여 사용할 수 있습니다.


하지만 위의 코드를 보면 약간 찝찝한 부분이 있습니다.

바로 생성해놓은 _instance 객체에 대한 해제를 하지 않아 메모리 Leak이 일어난것입니다.

실제로 Singleton 객체는 1개의 인스턴스만 생성되도록 하기때문에 메모리에 대한 Leak을 신경써야할 정도는 아니지만, 반드시 반납되어야 하는 외부 시스템자원 등을 사용하는경우엔 명시적으로 꼭 해제가 되어야합니다.


어떻게 하면 좋을까요? 소멸자에 코드를 추가?

안타깝지만 외부에서 소멸자를 직접 호출할 수 없도록 private으로 가둬놨네요

* new 로 생성된 코드는 delete 를 호출하기 전까진 절대 소멸자를 호출 하지 않습니다.


이때 이용할 수 있는 API가 atexit() 입니다.


함수 원형을 보면 다음과 같습니다.


int atexit(
   void (__cdecl *func )( void )
);


return type과 parameter가 모두 void인 함수 포인터를 전달받는 함수이며, 종료할 때 지정된 함수를 처리한다고 합니다. 또한 해당 API는 최대 32개까지 추가할 수 있으며 LIFO 형태로 마지막에 들어온 함수가 가장 먼저 실행된다.


소멸 코드를 추가한 Singleton Pattern


class singleton {
private:
    static singleton* _instance;

    singleton() {}
    singleton(const singleton& other);
    ~singleton() {}
public:
    static singleton* instance() {
        if (_instance == nullptr) {
            _instance = new singleton();
            atexit(release_instance);
        }
        return _instance;
    }
    static void release_instance() {
        if (_instance) {
            delete _instance;
            _instance = nullptr;
        }
    }
};


위에꺼보단 조금 더 다듬어진 코드가 완성되었습니다.






그렇다면 이렇게 생각해볼수도 있습니다.

왜 포인터로 만들어서 궂이 해제까지 하는 귀찮은 작업을 하지..? 그냥 static 멤버로 만들면 안되나?


why static?

* class에서 static member로 생성되면 class 객체가 몇개가 생성되던간에 1개의 member만 생성된다고 합니다.


그러면 궂이 memory leak 걱정도 안해도 되고 좋을 것 같네요!


class singleton {
private:
    static singleton _instance;

    singleton() {}
    singleton(const singleton& other);
    ~singleton() {}
public:
    static singleton& instance() {
        return _instance;
    }
};


오.. 뭔가 좀 더 간단해진 느낌이네요!


이렇게 하면 소멸자도 자동으로 호출 되겠네요


그런데 위 방법은 문제가 있다고 합니다.

Static class member 변수는 static 전역 변수와 마찬가지로 프로그램 시작시 main() 함수 호출 이전에 초기화가 된다 합니다. 그렇기때문에 만약 해당 클래스를 사용하지 않더라도 무조건 생성이 되는 현상이 발생되어 때에 따라서는 비효율적입니다.

그리고 때에 따라서 다른 전역 객체의 생성자에서 해당 member를 참조하고싶을수도 있을 때 문제가 발생할 수 있습니다.

바로 C++에선 전역 객체들의 생성 순서에 대해서 명확하게 정의하고 있지 않기 때문이죠

singleton class의 Instance는 아직 생성되지 않았는데 다른 전역 객체에서 instance를 참조를 하게되면 문제가 발생하겠죠


자 그럼 간단하게 하면서 어떻게 이 문제를 해결해볼까요?


아! member static을 지역 static으로 바꾸면 처음 함수가 호출될 때 생성되니까 어디서 접근을 시도하든 상관이 없겠네요!

* class에서 static  member 는 전역 객체와 같이 동작하지만 함수 내에 static 객체는 해당 함수가 호출되는 시점에 초기화가 됩니다.


class singleton {
private:
    singleton() {}
    singleton(const singleton& other);
    ~singleton() {}
public:
    static singleton& instance() {
        static singleton _instance;
        return _instance;
    }
};


단순히 member의 위치만 바뀌었는데 조금 더 안전한 코드가 되었다고 합니다.

* global static 객체와 Local static 객체의 차이가 이런 결과를 만들었네요.


하지만 위의 방법도 한가지 문제를 떠앉고있습니다.

바로 소멸자에서 해당 인스턴스를 참조하려 했을 경우 입니다.

C++에선 전역 객체의 생성 순서뿐만 아니라 소멸 순서에 대해서도 명확하게 정의하고 있지 않기때문에 이번엔 소멸시점에서 instance가 먼저 소멸해버렸다면.. 역시 똑같은 문제가 발생하게 됩니다.


Singleton pattern 특성상 완벽한 방법은 없지만 조금이라도 안전한 방법으로 구현해야하는건 맞는 것 같습니다.


하지만 앞서 얘기한바와 같이 Singleton Pattern은 아직도 문제를 가지고있습니다.

그중 한가지가 멀티쓰레드 환경에서 단일 인스턴스 생성이 보장 되지 못한다는 부분입니다. (thread safe issue)

이를 조금이나마 해소할 수 있도록 나온 기법이 DCLP (Double checked Locking Pattern) 이며,

DCLP를 적용하여 multi thread 환경에서 조금이나마 더 안전한 Singleton Pattern을 생성할 수 있는 방법은 다음에 소개하도록 하겠습니다.


반응형
저작자표시 비영리 동일조건 (새창열림)

'⌨ DEVELOPMENT > Design Pattern' 카테고리의 다른 글

[C++] DCLP - Double-Checked Locking Pattern  (2) 2015.12.19
[C++] RAII 패턴 - Resource Acquisition Is Initialization Pattern  (0) 2015.12.16

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [C++] DCLP - Double-Checked Locking Pattern

    [C++] DCLP - Double-Checked Locking Pattern

    2015.12.19
  • [C++] RAII 패턴 - Resource Acquisition Is Initialization Pattern

    [C++] RAII 패턴 - Resource Acquisition Is Initialization Pattern

    2015.12.16
다른 글 더 둘러보기

정보

웬디의 기묘한 이야기 블로그의 첫 페이지로 이동

웬디의 기묘한 이야기

  • 웬디의 기묘한 이야기의 첫 페이지로 이동

검색

메뉴

  • 홈
  • 태그
  • 방명록
  • 이야기

카테고리

  • 분류 전체보기 (204)
    • MY STORY (2)
    • 📸 WALKING WITH YOU (85)
      • 아이슬란드 신혼여행 이야기 (14)
      • 대한민국 구석구석 (62)
      • CONTAX N1 + T* 28-80mm (4)
      • SAMSUNG NX3000 (1)
      • 어느 멋진 날 (4)
    • ⌨ DEVELOPMENT (80)
      • BOOK:Review (1)
      • AI (13)
      • C++ (26)
      • Python (10)
      • WIndows Hooking (9)
      • Windows Kernel (3)
      • Design Pattern (3)
      • Debugging (9)
      • Tools (0)
      • Project (1)
      • Android (1)
      • 상업용 무료폰트 (4)
    • OS (4)
      • News (0)
      • Windows 일반 (4)
    • 모바일 (2)
      • 모바일 게임 (2)
    • 멘사 퍼즐 (9)
    • 생활 꿀 TIP (7)
      • 건강 (3)
      • 일상 (2)
    • 물생활 (8)
      • 골든볼 라미네지 롱핀 (8)
    • IT 기기 (2)
    • BLOG (4)
      • TISTORY BLOG TIP (3)

최근 글

인기 글

댓글

공지사항

아카이브

태그

  • 카페
  • windbg
  • AI
  • 아이슬란드
  • 해외여행
  • c++
  • 신혼여행
  • c

나의 외부 링크

  • kernel undocument api
  • 지구 관찰자의 일기
  • 지구와 지구곰

정보

WENDYS의 웬디의 기묘한 이야기

웬디의 기묘한 이야기

WENDYS

블로그 구독하기

  • 구독하기
  • RSS 피드

방문자

  • 전체 방문자
  • 오늘
  • 어제

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. © WENDYS. Designed by Fraccino.

티스토리툴바