웹서핑 조금만 해도 나오는 싱글턴 패턴 소스.
평소처럼 사용하다가 싱글턴 클래스 상속과 싱글턴을 상속받은 부모 클래스를 다시 자식 클래스가
상속을 받아서 쓰는부분에 대해 궁금함이 생겼다.

일반적인 싱글턴 소스들
template< class T >
class CLmSingleton
{
public:
	static T	*getInstance() {
		if( !s_pInstance ) s_pInstance = new T();
		return s_pInstance;
	}
	static void	releaseInstance() {
		if( s_pInstance ) { delete s_pInstance; s_pInstance = NULL; }
	}

protected:
	static T *s_pInstance;
};

template< class T > T *CLmSingleton< T >::s_pInstance = 0;

//-------------------------------------------------------------- template< class T > class CLmSingleton2 : private T { public: static T* getInstance() { // if use in multi-thread env, should add lock here. static CLmSingleton2<t> s_Instance; return& s_Instance; } protected: CLmSingleton2() {} virtual ~CLmSingleton2() {} }; #define SINGLETON(p) CLmSingleton2<p>::getInstance()

상단에 있는 싱글턴 소스는
class A : public CLmSingleton<A>
{
public:
	A() {}
	virtual ~A() {}

	int a;
};

A testClass;			// 일반적인 정적 변수 선언이 가능.
testClass.a = 99;		// 이것은 단순히 testClass 라는 클래스 멤버변수의 값만 바뀐다.
A* pA0 = testClass.getInstance(); // pA0의 멤버변수 a의 값은 쓰레기 값
A* pA1 = A::getInstance();
A* pA2 = CLmSingleton<A>::getInstance();
// pA0, pA1, pA2 세 포인터의 주소는 동일하다.
pA2->a = 98;			// pA0, pA1, pA2의 멤버변수 a가 전부 갱신됨.

testClass 와 포인터들은 다른 주소를 갖는다. 다만 testClass.getInstance() 로 불러오는 인스턴스만은 동일하다.
각각의 인스턴스는 동일하지만 ( testClass != 포인터 ) 이기 때문에 싱글턴이라 부르기 어렵다.
또한, 싱글턴의 특징 중 하나인 외부로 공개하지 않는 생성자와 소멸자가 public이다.
결국, 저 클래스는 실수로라도 정적 변수로 선언을 하게 된다면 나중에 문제가 커질 가능성이 있다.

혹시나 해서 생성자와 소멸자를 protected 와 private로 했는데, 생성자에 접근을 하지 못해 컴파일 에러가 뜬다.


두번째 테스트로 상속을 받지 않고 싱글턴을 적용해 보았다.
class B
{
public:
	B() {}
	virtual ~B() {}

	int b;
};

B* pb0 = B::getInstance(); // 상속을 받지 않았으므로 B에는 getInstance()란 메소드가 없어 오류
B* pb1 = CLmSingleton<B>::getInstance(); // B 클래스라는 새로운 인스턴스 생성 성공.


마지막 테스트는 상단 소스중 CLmSingleton2 로 했다.
class C
{
public:			// protected 도 가능
	C() {}
	virtual ~C() {}

	int c;
};

class C : public CLmSingleton2<C> 로 선언 자체가 안된다. 소스를 보면 서로가 서로를 상속 받는 구조가 되어버린다.

두번째 싱글턴의 경우, 그 특징은 사용하려 명시한 클래스의 자식클래스로 상속이 된다. 첫번째는 싱글턴 자체가 부모클래스이고 사용하고 싶은 클래스는 자식클래스로 상속을 시키는 구조다.

이 싱글턴은 단지 아래 방법만이 가능하다.
C* pc0 = SINGLETON(C);
C* pc1 = SINGLETON(C);

두번째 싱글턴의 경우 클래스 생성자가 public이든 protected든 인스턴스가 생성이 된다. protected인데 생성이 되는 이유는 싱글턴이 부모의 생성/소멸자를 상속을 받았기에 내부에서는 부모를 참조할 수 있기 때문이다. private의 경우 생성/소멸자를 상속 시키지 않아 컴파일 에러가 난다.

//----------
그리고 두 싱글턴의 차이를 보자면 첫번재는 동적 인스턴스이다. 가져올때 없으면 new로 할당을 하고 존재한다면 이미 만들어진 인스턴스를 가져온다. 하지만 소멸자는 불리지 않아 인스턴스가 제대로 지워졌는지 확인이 안된다.
아니면 releaseInstance()를 호출하는 수 밖에 없다.

하지만 두번째 싱글턴은 정적 인스턴스이기 때문에 클래스 자체가 파괴가 되거나 프로그램이 종료가 된다면 메모리를 반환을 한다. 메모리 해제를 할 필요가 없다는 것이다.
그리고 첫번째 싱글턴처럼 인스턴스를 생성할 때 여러 방법이 불가능하다. 싱글턴 생성이나 인스턴스를 가져옴에 있어 통일성이 있다.
만약, 첫번째 싱글턴처럼 이렇게 만드나 저렇게 만드나 똑같은 인스턴스가 나온다면 인스턴스를 가져올 때 통일적이지 않아 나중가면 햇갈릴 가능성이 있는 것이다.

개인적으로 두번째 싱글턴이 좋다.

이 프로그램은 Sleep( 2000 )을 걸고! 퍼포먼스 카운터와 어셈블리 카운터와 타임겟타임을 비교해본것임.

순서대로 curTime 은 Performance Counter로 계산한 Elapsed time 이라고 할까요? 단위는 ns
part pc 는 ms단위의 Performance Counter,
curRe 는 앞에는 Performance Counter, 뒤에는 Assembly count 인데, 바로 전 상황과 현재의 차이.
asm pc 는 Assembly counter,
tgt.. timeGetTime
마지막 pc 는 순수한 Performance Counter의 값.

curRe 에서 보이듯이 어셈으로 테스트 했을땐 sleep( 2000 )에 맞게 딱딱 떨어졌는데,
퍼포먼스 카운트는 정확하게 2000으로 떨어지지가 않는다.( 요기도 역시 ns 단위 )

중간에 한번 변위가 엉망인 이유는 듀얼 코어.. 크흑.

확실히 퍼포먼스 카운터가 정밀도가 높아서 2초 단위로 찍어도 ns에서 나온 결과를 보면 수치가 조금씩 다르다.
멋쟁이 정밀도. 대신에 그 만큼 느리다는 단점은 널리 알려진 것이고.
근데.. 온라인 게임에서 쓰기에 ns단위까지 갈 필요가 있을까 라고 개인적으로 생각해본다.
그래서 개인적으로 timeGetTime을 주로 쓰는데 가끔 테스트로 GetTickCount 역시 사용하고. ㅋ
하지만 정밀도는 조금 버리는 한이 있더라도 역시 게임은 반응이 빨라야 재밌지. 함수 호출하는 시간을 조금이라도 더 아껴서
빠른 반응을 보인다면 약간의 딜레이정도는 무난하지 않을까 싶다. 그런의미에서 이제는 어셈 카운터가 원츄~!

참고로. performance counter 와 assembly counter 는 __int64형 timeGetTime은 DWORD형.
참고2. 어셈카운터는 ns 에서 0이 하나 더 붙어있는 값으로 나오더라 ㅋㅋ
C:\unigine\externs\bin;C:\unigine\lib;C:\unigine\bin;C:\Python26;"C:\Program Files\Microsoft DirectX SDK (October 2006)\Utilities\Bin\x86";%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;c:\Program Files\Microsoft SQL Server\90\Tools\binn;C:\Program Files\Microsoft SQL Server\90\Tools\binn;C:\Program Files\TortoiseSVN\bin;C:\Program Files\Common Files\Compuware;C:\Program Files\Microsoft SQL Server\90\Tools\binn\;C:\Program Files\Unigine\Evaluation Kit\tools\x86

위에는 실제로 사용하는 나의 path 경로 였었음.
헌데 scons를 쓰려고 하닌까 자꾸만 타이틀의 오류가 나면서 안되길래 한참을 고민했는데,
결국 찾아낸 문제의 부분!! "C:\Program Files\Microsoft DirectX SDK (October 2006)\Utilities\Bin\x86";
dx가 두가지가 깔려있는데 2006 8월이랑 2009 3월버전. 물론 2006 8월은 쓰이질 않고 지우는걸 깜박해서 그냥 냅뒀었는데, 그게 문제가 될줄이야.
요기까지는 잡설이었고.

요점! path 잡을때 ( ) 가 들어가면 링크가 엉망이 됨....
젠장 (October 2006)...
예상되지 않았다는 오류가 난다면 자신의 컴퓨터 path가 제대로 되어있는지 확인을 해보자. 혹시 몰라 검색해보니, path가 안 잡혀 있는 상태로 뭔가를 하려 할때도 난다고 하는데 그 부분은 아직 헤딩해보질 못해서 잘 모르겠다.

내컴터 오른쪽클릭 - 시스템 등록 정보 - 고급 - 환경변수 - 시스템변수 - path에 가면 자신의 윈도우 path를 수정할 수 있다.
글타고 막 수정하진 말자. 언제나 백업을 위주로~!

포스팅 할 예정이 아니어서 인증샷찍는걸 깜박..;
std::map< int, int > Map1;
이런녀석이 있다고 할때 Map1[1], Map1[2] 처럼 접근이 가능하다. 물론 리턴값은 first에 맞는 second

그런데 std::map< int, int > *pMap2;
이렇게 선언되어있는 녀석이 있다면 pMap2[1], pMap2[2] 의 접근이 불가능하다.
이유인 즉슨!!

일반 map 에서 정의되어 있는 [] 오퍼레이터가 제대로 작동하지 않아서이다.
포인터 map에서의 []는 int *pI = new int[10]; 에서의 pI[1] 와 같은 의미.

결국 map 이라는 녀석이 할당된 메모리 주소를 쭉 찾아가다보니 당연히 first 값을 넣어도 second가 나오지 않는다.

( 확실한건 아니고 나의 가설일뿐 ㅎㅎ )

그렇다면 포인터로 선언한 map은 쓸수 없는가?
당연히 사용이 가능하다. 세가지 방법중 맘에 드는걸로 쓰는걸 추천~

1. 레퍼런스를 만들어 참조한다.
std::map< int, int > &refMap = *pMap2;
- 장점은 일반 map 사용하듯이 쓰면 된다.
- 단점은 주소 복사연산이기 때문에 변수 생성시에만 가능하다.
std::map< int, int > &refMap; << 선언 에러
std::map< int, int > refMap;
&refMap = *pMap2; << 말도 안되는 연산. 당연히 에러

2. 주소를 열심히 찾아가는 방법을 쓴다.
int iSecond = (*pMap2)[0];
- 장점은 레퍼런스가 아니니 때문에 어디서든 map 포인터에서 second의 값을 불러올 수 있다.
- 단점은 아직 못 찾았는데, 개인적으로 미관상 소스가 이쁘지 않다! ㅋㅋㅋ

3. 이터레이터를 사용한다면 아무 문제없다.
포인터든 아니든 이터레이터는 진리.

post script.
map은 [] 오퍼레이터 내부에서 find 와 insert를 돌린다. find로 있으면 second리턴 없으면 insert
( 이걸 모른다면 map 공부를 다시 하시길.. ㅋ )
위에 쓴 예시는 내부에 값이 있다는 전제하에 쓴 글임니당.

vector나 list에서도 같은 문제가 발생하겠죵? 테스트를 안해봐서 이건 모릅니다! ㅋ
#ifdef WIN32
#define TRACE(fmt, ... ) Func( fmt, __VA_ARGS__ )
#else
#define TRACE(fmt, args...) Func( fmt, ##args )
#endif

파라메터(parameter) : 파라메터는 함수에서 정의한 인자를 의미한다.

예 >
void swap( int a, int b );   // 여기서 a, b는 파라메터 이다.

아규먼트(argument) : 아규먼트는 함수에 넘겨줄 변수나 값을 의미한다.

예 >
int x, y;
x = 10;
y = 20;
swap( x, y );  // 여기서 x, y는 아규먼트가 된다.