[질문] 싱글톤 구현 방법

프로그래밍 일반에 관한 포럼입니다.

Moderator: 류광

Locked
비회원

[질문] 싱글톤 구현 방법

Post by 비회원 »

보통 싱글톤을 구현할 때 아래 처럼 인스턴스의 포인터를 넘겨주는 방식을 사용하더군요
static CSingleton* Instance() { return &m_instance; }

어떤 분들은 참조를 넘기기도 합니다.
static CSingleton& Instance() { return m_instance; }

개인적으로 생성/삭제 시기가 정해져 있는

싱글톤 객체에 대해서 포인터를 사용하는 방법은 불필요하다고 생각되는데,

책의 에제나 일반적으로 짜여진 코드를 봤을 때 대부분 포인터를 사용하는데에는 어떤 이유가 있으리라고 생각되는데 그 이유가 무엇인가요?
비회원

..

Post by 비회원 »

질문자입니다. 위에 예시로 든 싱글톤 코드에요....

class CSingleton
{
public:
static CSingleton& Instance() { return m_instance; } // 혹은 static CSingleton* Instance() { return &m_instance; }
private:
static CSingleton m_instance;
}
비회원

-0-

Post by 비회원 »

포인터로 넘기는 경우이유라면야..

if( CSingleton::Instance() == NULL )

이라는 식으로 사용할 수 있으니까요.


하지만 인스턴스가 static 으로 정의 되어있는경우에야.. 굳이 포인터로 넘길 필요가 없곘죠.

사실 예제처럼 static 으로 정의하고 사용할거라면,, 그냥 ms_Instance를 public으로 해서 사용하는거와 다를게 없죠. 결국 곧 싱글톤을 쓸 필요가 없다는게 되는거죠 -_-



class CClass
{
protected:
CClass() {}

public:
virtual ~CClass() {}

public:
CClass* Instance() { return ms_instance; }

private:
static CClass* ms_instance;

public:
virtual void Run() PURE;

};

class CMyPrintClass : public CClass
{
private:
void Run() { printf("-_-"); }

}


void Main()
{
new CMyPrintClass();

if( CClass* pInstance = CClass::Instance() )
pInstance->Run();

delete CClass::Instance();
}

이정도면 싱글톤을 사용하는 의미가 좀 더 있지 않을까요..
비회원

Re: -0-

Post by 비회원 »

비회원 wrote:포인터로 넘기는 경우이유라면야..

if( CSingleton::Instance() == NULL )

이라는 식으로 사용할 수 있으니까요.


하지만 인스턴스가 static 으로 정의 되어있는경우에야.. 굳이 포인터로 넘길 필요가 없ㅤㄱㅖㅆ죠.

사실 예제처럼 static 으로 정의하고 사용할거라면,, 그냥 ms_Instance를 public으로 해서 사용하는거와 다를게 없죠. 결국 곧 싱글톤을 쓸 필요가 없다는게 되는거죠 -_-



class CClass
{
protected:
CClass() {}

public:
virtual ~CClass() {}

public:
CClass* Instance() { return ms_instance; }

private:
static CClass* ms_instance;

public:
virtual void Run() PURE;

};

class CMyPrintClass : public CClass
{
private:
void Run() { printf("-_-"); }

}


void Main()
{
new CMyPrintClass();

if( CClass* pInstance = CClass::Instance() )
pInstance->Run();

delete CClass::Instance();
}

이정도면 싱글톤을 사용하는 의미가 좀 더 있지 않을까요..
싱글톤의 핵심개념중 하나가 자기 클래스로 된 객체가 1개임을 보장하는 것인데,
위의 예제를 보면 그것을 보장하지 못하고 있네요.
싱글톤을 사용하는 의미가 있는것이 아니라 오히려 싱글톤을 제대로 사용하지 못하고 계신 것 같습니다.
aronze
Posts: 14
Joined: 2008-08-18 02:14

포인터를 사용하는 이유는..

Post by aronze »

일반적인 상황에서는 님이 말씀하신 바와 같이 포인터를 사용할 필요가 없습니다. static instance 에 대해 NULL 체크를 할 필요도 없고요.

제가 알기로 포인터로 싱글톤을 사용하는 것은 딱 하나의 이유가 있습니다. 바로 dll 이죠. 만약 어떤 라이브러리를 배포해야 하는데 그 안에 싱글톤이 있다면 어떻게 될까요? dll 에서 생성된 정적 변수가 어디에 있나요?

이런 경우 싱글톤을 사용하려면 아이러니하게도 new 를 통해 싱글톤 객체를 한번 생성해 주는 방법밖에 없습니다. Ogre3D 엔진을 보면 모든 싱글톤을 이런 식으로 사용하고 있어 저도 처음에는 의아했습니다만, 저도 dll 로 배포하려고 하면서 보니.. 그래서 그렇게 했구나.. 더군요. 물론 템플릿으로 배포하고 최종 클라이언트가 조립하게 하는 방법도 있겠습니다만, 우리가 원하는 형태가 아니죠:D
비회원

Re: 포인터를 사용하는 이유는..

Post by 비회원 »

aronze wrote:일반적인 상황에서는 님이 말씀하신 바와 같이 포인터를 사용할 필요가 없습니다. static instance 에 대해 NULL 체크를 할 필요도 없고요.

제가 알기로 포인터로 싱글톤을 사용하는 것은 딱 하나의 이유가 있습니다. 바로 dll 이죠. 만약 어떤 라이브러리를 배포해야 하는데 그 안에 싱글톤이 있다면 어떻게 될까요? dll 에서 생성된 정적 변수가 어디에 있나요?

이런 경우 싱글톤을 사용하려면 아이러니하게도 new 를 통해 싱글톤 객체를 한번 생성해 주는 방법밖에 없습니다. Ogre3D 엔진을 보면 모든 싱글톤을 이런 식으로 사용하고 있어 저도 처음에는 의아했습니다만, 저도 dll 로 배포하려고 하면서 보니.. 그래서 그렇게 했구나.. 더군요. 물론 템플릿으로 배포하고 최종 클라이언트가 조립하게 하는 방법도 있겠습니다만, 우리가 원하는 형태가 아니죠:D
dll 에서 생성된 정적변수에 대한 포인터나 레퍼런스나 개념적으론 동일한 것 아닌가요?
비회원

Re: 포인터를 사용하는 이유는..

Post by 비회원 »

비회원 wrote:
aronze wrote:일반적인 상황에서는 님이 말씀하신 바와 같이 포인터를 사용할 필요가 없습니다. static instance 에 대해 NULL 체크를 할 필요도 없고요.

제가 알기로 포인터로 싱글톤을 사용하는 것은 딱 하나의 이유가 있습니다. 바로 dll 이죠. 만약 어떤 라이브러리를 배포해야 하는데 그 안에 싱글톤이 있다면 어떻게 될까요? dll 에서 생성된 정적 변수가 어디에 있나요?

이런 경우 싱글톤을 사용하려면 아이러니하게도 new 를 통해 싱글톤 객체를 한번 생성해 주는 방법밖에 없습니다. Ogre3D 엔진을 보면 모든 싱글톤을 이런 식으로 사용하고 있어 저도 처음에는 의아했습니다만, 저도 dll 로 배포하려고 하면서 보니.. 그래서 그렇게 했구나.. 더군요. 물론 템플릿으로 배포하고 최종 클라이언트가 조립하게 하는 방법도 있겠습니다만, 우리가 원하는 형태가 아니죠:D
dll 에서 생성된 정적변수에 대한 포인터나 레퍼런스나 개념적으론 동일한 것 아닌가요?
정정합니다.

dll 에서 new 로 생성된 변수에 대한 포인터나 정적변수에 대한 레퍼런스나 개념적으론 동일한 것 아닌가요?
aronze
Posts: 14
Joined: 2008-08-18 02:14

dll 에 정의된 정적 변수는..

Post by aronze »

이 내용은 생각보다 방대한 내용을 담고 있어 제가 다 설명 드리기는 (능력상) 어려울 것 같습니다만, 간단하게만 정리해 보도록 하겠습니다. 멀티 쓰레딩이니 멀티 프로세싱이니 하는 이야기는 다른 고수 분들이..

정적 변수를 dll 쪽에 선언하게 되면 일반적인 경우 exe 쪽에서 이 변수가 언제나 한번만 선언되어 있다는 보장을 할 수 없습니다. 실제로 간단한 싱글톤 클래스를 dll 로 만드시고 이 dll 을 사용하는 exe 를 만들어 보시면 원하는 동작을 할 수 없다는 것을 알게 될 겁니다.

예를 들어, dll 자체에 static 에 대한 메모리 공간이 있고 exe 쪽에서는 이 영역을 보지 못하는 경우 등이 생기는 것이죠.

따라서, new 를 통해 싱글톤에 대한 메모리를 직접 만들거나 하는 인스톨 방식을 사용하는 것입니다. 물론 단순히 new 만으로 사용하는 경우, 생성시간 지연이라는 singleton 의 장점을 버리게 되는 것이기에 보통 getSingletonPtr() 내부에 ptr 이 NULL 인 경우 생성해라 라는 코드를 넣기도 합니다.

하지만 이 경우 쓸데 없는 오버헤드를 가져오므로 Ogre3D 같은 경우 일일이 new 로 생성을 하고 있습니다.(보통 Root 라는 녀석 하나를 생성하면 기본 싱글톤 들을 모두 깨웁니다)

보다 자세한 내용이 궁금하시면 google 에서 singleton, static variable, dll 등의 검색어 조합으로 찾아보시면 원하는 내용을 찾을 수 있을 것 같습니다.

전에 디버거 돌려보면서 알게 된 내용인지라 설명이 미흡한점 죄송합니다.
lancekun
Posts: 62
Joined: 2008-06-06 02:04
Location: 호드
Contact:

Post by lancekun »

디자인패턴처음배워서 적용해봤을당시.. 싱글턴사용했을때 DLL쪽 메모리랑 프로그램쪽 메모리가 달른걸 모르고 한참헤맸었던 기억이 ㅠ_ㅠ...
안녕하세요~ 랜스군입니다. 모두 열프 '_'

랜스군의 게임공작소
http://lancekun.com/
비회원

Post by 비회원 »

이펙티브 C++ 보시면 관련된 내용이 있습니다~~

http://hankiya.com/tc/285 참조하시면 될듯
aronze
Posts: 14
Joined: 2008-08-18 02:14

이의 있습니다.

Post by aronze »

링크는 잘 읽어 보았습니다만( 다 맞는 말이고요) 이의가 있습니다.

유명한 라이브러리도 이런 방식으로 싱글톤 소멸 시기에 이런 저런 일을 한다는 것도 보았습니다만, 이처럼 순서가 중요한 녀석들이 있다면 정상 종료 루틴을 통해 릴리즈가 되어야 하는 것이지 싱글톤의 소멸 시점에 날라가는 것은 디자인 적으로 문제가 있다고 생각합니다.

요는 이런 상황을 극복할 수 있는 방법은 싱글톤을 heap 에 넣지 않고도 해결 할 수 있는 디자인 요소가 있으며 더 나아가 싱글톤을 최후의 최후까지 살아남게 만드는 방법도 사용할 수 있습니다.(모던cpp)
aronze
Posts: 14
Joined: 2008-08-18 02:14

Post by aronze »

싱글톤이 가지고 있는 객체의 소멸 시기를 정하는 것은 앞서 말씀 드렸던 바와 같이 적절한 순간에 싱글톤 객체의 destroyXXX() 등을 통해 제거 하는 것입니다; 매우 정상적으로요. 비정상 상황이 있다면 역시 예상하여 비정상 상황에 대한 처리를 해주어야 하고요. 어떤 부분을 말씀하신 것인지 혼돈되어서 중복 내용이지만 이 부분을 먼저 적어 봅니다.

궁금하신 부분은 싱글톤 객체가 끝까지 살아 남는 방법일 것이라고 생각되는데요, 모던 cpp 의 싱글톤 부분을 보면 피닉스 단위 정책이라는 부분에 나옵니다. 간단하게 말씀드리면 코어 api 를 사용해서 죽은 넘을 다시 메모리에 올리는 방법인데요,

http://www.geocities.com/rani_sharoni/LokiPort.html

에서도 소스를 다운로드 받아 보실 수 있습니다. 아무 버전이나 받으시고 singleton.h 를 보시면요 PhoenixSingleton 이라는 녀석이 있는데요, 핵심은 요기에 있는 std::atexit() 에 있습니다. 이 함수에 함수 포인터를 등록할 수가 있는데요, 등록된 녀석은 프로그램이 종료되고 가장 마지막에 호출됩니다. 여기다가 뻘짓을 할 수 있는 함수를 꼽아두는 것이죠. 책이 있으면 좀 더 자세히 설명 드리겠는데, 여기까지가 한계네요. 죄송합니다. 보다 자세한 내용은 고수님들께서..
gimmesilver
Posts: 85
Joined: 2005-10-23 05:46
Location: NCsoft openmaru studio
Contact:

...

Post by gimmesilver »

비회원

ㄷㄷ

Post by 비회원 »

추상클래스에 싱글톤을 넣어두고, 헤더만 참조해서 링크하고 실행하기 위해 사용할수도 있고, 그냥 코딩하기 편하자는 용도로 사용하기도 하는데요. 싱글톤이 항상 유효하다고 단정할수도 없고, 그래야할 이유도 없습니다. 그냥 어떠한 클래스에 붙느냐에 따라 성격이 바뀌는거죠. -_-;

질문자의 질문으로 다시 돌아가서 생성, 삭제 시기가 정해져있고 변하지 않는다면 뭐 그냥 래퍼런스를 반환하면 될일이죠. 하지만 그 인스턴스가 때로는 NULL을 반환할 경우에도 존재하는 클래스에 붙을수도 있지않습니까?

물론, 하나의 프로젝트에 두가지 형태의 싱글톤 클래스를 만들고 사용한다면, 다른 사용자가 헤더만 보고 이 클래스의 성격을 파악하는데 도움이 될껍니다. ㅋㅋ 하지만 보통 하나정도만 만들겠죠. 래퍼런스를 반환하는 타입이라면 후자의 경우에는 문제가 되니 자연스레 포인터를 반환하는 형태로 만든다고 생각하시면 되지 않을까요
쌀밥
Posts: 1058
Joined: 2003-02-02 20:23
Location: THQ Inc.
Contact:

Post by 쌀밥 »

싱글톤 문제는 이미 gpg에서도 수십 차례 이야기 되었던것 같은데, 검색해보시면 좋은 글들을 찾으실수 있을듯 합니다.

그리고 보다 자세한 논의는 modern C++ design 이라는 책에서 (수십 페이지에 걸쳐) 깊이 있게 다루고 있고, 구현된 라이브러리도 Loki 에서 제공하고 있습니다.

싱글톤 소멸은 그냥 소멸시키는 걸로 끝나는게 아니라 생성되는 순서와 소멸되는 순서등 훨씬 복잡합니다. 상황에 따라 다른 형태의 전략을 사용해야하기 때문에 딱히 이것이 맞다 저것이 맞다 하고 고정해서 쓸수도 없겠습니다.
I want to live in korea, making programs, but...
http://wrice.egloos.com
Locked