비공개2011. 7. 7. 16:28

항목 5. C++이 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자

C++의 어떤 맴버 함수는 우리가 클래스 안에 직접 선언해 넣지 않으면 컴파일러가 저절로 선언해 주도록 되어 있습니다.
이때 컴파일러가 만드는 함수( 복사 생성자 / 복사 대입연산자 / 소멸자 )는 모두 기본형입니다.

 

 

1
class Empty{};

 

 

1
2
3
4
5
6
7
8
9
10
class Empty
{
public :
        Empty() { }
        Empty( const Empty& ) {       }
        ~Empty() {}
 
        Empty& operator=( const Empty& ) { }
 
};


는 서로 같습니다.

 

기본코드가 만들어지는 조건을 만족하는 코드!!!

 

Empty e1;       // 기본 생성자, 소멸자 

Empty e2(e1); // 복사 생성자 

e2 = e1;         // 복사 대입 연산자

 

 

 

기본 생성자

 

생성자가 존재한다면 기본 생성자는 컴파일러가 자동으로 만들어내지 않는다.

 

 

 

 복사 생성자 / 복사 대입 연산자

 

1) 컴파일러가 자동적으로 생성해주는 복사 대입연산자의 기능은 그저 비정적 데이터를 복사하는 일이다.

 

2) 컴파일러는 컴파일도중 일정한 조건이 성립되어야만 복사 대입 생성자를 만들어준다.

     만약, 맴버 중에 참조자가 있는 경우 복사 대입 연산자는 사용자가 정의 해줘야 한다. 

 

3) 복사 대입 연산자를 private로 선언한 기본 클래스로부터 파생된 클래스의 경우,

    이 클래스는 암시적 복사 대입 연산자를 가질 수 없다.




소멸자

 

소멸자는 이 클래스(빈 클래스)가 상속한 기본 클래스의 소멸자가 가상 소멸자로 되어 있지 않으면
역시 비가상 소멸자로 만들어진다.








항목 6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자

 

컴파일러에서 자동으로 제공하는 기능을 허용하지 않으려면... 해결법은???

 

해결법 1 ) private으로 복사 생성자, 복사 대입 연산자를 선언만 하기

 

1
2
3
4
5
6
7
8
9
class HomeForSale
{
public :
             ...
 
private :
             HomeForSale( const HomeForSale& ); // 선언만 존재
             HomeForSale& operator=( const HomeForSale& );
};

 

- 다른 부분에서 해당 함수를 호출하려 할 경우 Linke Error!!!

 

해결법 2 ) Base클래스에서 선언하고 그것을 파생클래스에서 상속받게 하기

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class UnCopyAble
{
protected :			// 파생된 객체에 대하여
	UnCopyAble()  {}		// 생성과 소멸을
	~UnCopyAble() {}		// 허용합니다.

private :			// 하지만 복사는 방지합니다.
	UnCopyAble( const UnCopyAble& ); 
	UnCopyAble& operator=( const UnCopyAble& );

};

class HomeForSale : private UnCopyAble
{
	...
};

 

- Link Error Compile Error로 바꿈

 

- 참고로, Boost 라이브러리의 noncopyable 클래스는 위의 내용(UnCopyAble)의 기능을 제공한다.










항목 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자.

 

연관된 객체들 간의 상속된 관계속에서 객체 소멸시 소멸자가 가상소멸자가 아닌 경우

소멸 순서에 의한 문제가 발생할 수 있다.

 

 어떤 경우에 가상 소멸자를 써야 할 것인가?

다형성을 가진 기본 클래스에는 반드시 가상 소멸자를 선언해야 합니다.
어떤 클래스가 가상 함수를 하나라도 갖고 있으면이 클래스의 소멸자도 가상 소멸자 이어야 합니다.
 

반대로기본 클래스로 설계되지 않았거나 다형성을 갖도록 설계되지 않은 클래스에는
가상 소멸자를 선언하지 말아야 합니다.

 

■ 무작정 가상 소멸자를 쓰면 안되는 이유

가삼 함수 테이블 포인터라는 별도의 자료구조가 포함되어 객체 사이즈가 커지게 된다.

때문에, 프로그램 실행환경 즉, 32/64비트 아키텍처 중 상황에 따라 퍼포먼스 / 이식성 등의 영향을 미치게 된다.

 

 가상 소멸자가 없는 STL 컨테이너
 
 

Vector , list, set, tr1::unordered_map

 

 Tip : 어떤 클래스가 추상 클래스였으면 좋겠는데 마땅히 넣을 만한 순수 가상 함수가 없을 때

 

1

2

3

4

5

class AWOV

{

public :

               virtual ~AWOV() = 0;

};









항목 8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

  

소멸자 내에서 예외가 발생하면 그 후부터 프로그램이 정상적으로 작동하리라는 보장을 잃게 된다.

 
1
2
3
4
5
6
7
8
9
10
11
12
class Widget
{
public :
	...
	~Widget(){ ... } // 소멸자에서 예외가 발생한다고 가정합니다.
};

void doSomething()
{
	std::vector<Widget> v;
	...
} // v는 여기서 자동으로 소멸합니다.

 

- 만약 v 10개의 Widget 객체 정보를 담고 있다고 가정하자.

v는 소멸 시점에서 순차적으로 소멸자를 호출 할 것이다.

이때 객체 소멸자를 호출하다가 예외가 발생한다면?

 

 소멸자 내에서 예외 발생

1) 무시        (X)     -> 문제 발생 여지 생김

2) 사용자 처리 (O)

 

 예외 발생 처리 기본  방법  제안

  1) 프로그램을 바로 끝내버립니다.

2) 예외를 삼켜 버립니다.

 

 좀 더 나은 방법   

발생 할 소지가 있는 문제에 대처할 기회를 사용자에게 제공

Point > 예외가 발생한다면 소멸자가 아닌 함수에서 발생하도록 해야 한다.

 

■ 책 요약 

소멸자에서는 예외가 빠져나가면 안 됩니다. 만약 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이든지 소멸자에서 모두 받아낸 후에 삼켜 버리든지 프로그램을 끝내든지 해야합니다.

어떤 클래스의 연산이 진행되다가 던진 예외에 대해 사용자가 반응해야 할 필요가 있다면, 해당 연산을 제공하는 함수는 반드시 보통의 함수(, 소멸자가 아닌 함수) 이어야 합니다






 

항목 9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자

  
■  생성자에서 가상함수를 호출하는 경우 하나!
 

class Transaction
{
public :
	Transaction() { Init(); }

	virtual void logTransaction() const = 0; // 순수가상함수

	...

private :
	void Init()
	{
		...
		logTransaction(); // virtual 함수 호출???
	}
};

class BuyTransaction : public Transaction
{
public :
	virtual void logTransaction() const ;

	...
};


- 생성자에서 초기화를 할때 아무함수나 신경안쓰고 호출해도 되겠지? 아래 사항을 고려해보자.

1) BuyTransaction 객체가 생성 될 때 Base 클래스인 Transaction 생성자가 먼저 호출 된다.

2) Base 생성자 호출 시점에서는 파생 클래스들의 정보는 초기화가 안되어있다. ( 미정의 )

3) 파생 클래스의 기본 클래스가 생성되는 동안은, 그 객체의 타입은 바로 기본 클래스이다.
    호출되는 모든 가상 함수는 Base 클래스( Transaction )의 것으로 결정된다.

    즉, 이때 순수가상함수를 호출하게 된다. -> 프로그램 종료

4) 컴파일시 에러조차 발생하지 않는다.
   ( Transaction 생성자 호출시!! Init() 아닌 직접 virtual void 
logTransaction() 함수를 사용하면 Link 에러 발생 )



■ 해결법 하나!

 
 

class Transaction
{
public :
	explicit Transaction( const std::string& logInfo );

virtual void logTransaction( const std::string& logInfo ) const; // 비가상 함수 ... }; Transaction::Transaction( const std::string& logInfo ) { ... logTransaction(logInfo); // 비가상함수 호출 } class BuyTransaction : public Transaction { public : BuyTransaction( param ) :Transaction( createLogString( param ) ) // 로그 정보를 기본 클래스 생성자로 넘김 { ... } ... private : static std::string createLogString( param ); // 도우미 함수 };


1) 필요한 초기화 정보를 파생 클래스 쪽에서 기본 클래스 생성자로 '올려'주도록 하자( 도우미 함수 활용 )

2) 기본 생성자에서 절대로! 가상 함수 호출은 하지 않도록하자. 다른 방법 강구!!



■ 책 요약

생성자 혹은 소멸자 안에서 가상 함수를 호출하지 마세요.
가상 함수라고 해도, 지금 실행중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않으니까요.



 


 
 

항목 10. 대입 연산자는 *this의 참조자를 반환하게 하자

 

1
2
3
4
5
6
7
8
9
10
class Widget
{
public :
	...
	Widget& operator=( const Widget& rhs ) // +=, -=, *= 에도 동일한 규약이 적용
	{
	        ...
	      return *this;
	}
};



1
2
3
Widget a,b,c;

a = b = c; // 이런 연산이 가능케 한다.


■ 이것은 관례이다.

모든 기본제공 타입들이 따르고 있을 뿐만 아니라 표준 라이브러리에 속한 모든 타입에서도 따르고 있다.

고로, 나도 따르자. ㅎㅎ









항목 11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자   


코딩을 하다보면 자기대입의 경우가 발생할 수 있다.
이때 문제가 발생할 수도 있는데 다음과 같은 경우이다.


1
2
3
4
5
6
7
8
// 안전하지 않게 구현된 코드
Widget& Widget::operator=( const Widget& rhs )
{
	delete pBmp;			// 현재의 비트맵 사용을 중지합니다.
	pBmp = new Bitmap(*rhs.pBmp);	// 이제 rhs의 비트맵을 사용하도록 만듭니다.

	return *this;
}


만약 위 코드에서 삭제되는 pBmp 와 인자로 받는 rhs,pBmp가 같은 객체라면?
4번 라인에서 이미 pBmp는 삭제된다. 
5번 라인에서 인자로 받는 정보를 가지고 새로운 Bitmap을 생성해야한다라면
      pBmp는 결국 어떻게 될 것인가???
      다는 몰라도 분명한건 시스템을 불안정한 상태로 만드는 것임에는 틀림없다.

책에서는 자기대입의 위험성에 대한 몇가지 대안을 제시한다. 

1) 일치성 검사

1
2
3
4
5
6
7
8
9
Widget& Widget::operator=( const Widget& rhs )
{
	if( this == &rhs ) return *this; // 객체가 같은지 검사하여 일치할 경우 아무일도 안한다.

	delete pBmp;					
	pBmp = new Bitmap(*rhs.pBmp);	

	return *this;
}


2) 예외 안정성 보완

1
2
3
4
5
6
7
8
9
Widget& Widget::operator=( const Widget& rhs )
{
	Bitmap *pOrig = pBmp;	    // pBmp를 가르키는 포인터를 준비합니다.

	pBmp = new Bitmap( *rhs.pBmp ); // pBmp는 새로운 객체를 가리킵니다.
	delete pOrig;		    // 원래 pBmp가 가리키는 객체를 삭제합니다.

	return *this;
}

이 방법은 Bitmap을 동적할당하여 생성할 때 문제가 발생했다하더라도, 원본 Bitmap 데이터는 보존이 된다고 책은 말합니다.

허나, 제가 알기론 동적할당시 문제가 발생하여 예외가 발생하였을 경우! 프로그램이 죽거나, NULL을 반환하고
계속 진행하는 것으로 알고 있습니다. 결국 6번라인은 실행이 되는 셈이지요. 그렇다면 데이터는 보존이 되지 않습니다.
뭔가 심도깊고 유식해보이는 방법일지언정 현업에서는 1) 일치성 검사 수준의 대응이 적절하지 않을까 생각해봅니다.

3) Copy and Swap

1
2
3
4
5
6
7
8
Widget& Widget::operator=( const Widget& rhs )
{
	Widget temp( rhs ); // rhs의 사본 하나 생성

	swap(temp);         // *this 의 데이터를 위의 사본과 맞바꿈

	return *this;
} 

임시 객체를 생성한 후 객체의 내용을 맞바꾸는 방식




■ 책 요약

operator=을 구현할 때, 어떤 객체가 그 자신에 대입되는 경우를 제대로 처리하도록 만듭시다. 

원본 객체와 복사대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조정할 수도 있으며, 
복사 후 맞바꾸기 기법을 써도 됩니다. 두 개 이상의 객체에 대해 동작하는 함수가 있다면, 
이 함수에 넘겨지는 객체들이 사실같은 객체인 경우에 정확하게 동작하는지 확인해 보세요.  









항목
 12. 객체의 모든 부분을 빠짐없이 복사하자


class User : public CObject // 상속이 안되있다고 가정!
{
public :

	// 기본 생성자
	explicit User()
		: name( "None" )
	{
		/* Nothing */
	}

	explicit User( const std::string& _data )
		: name( _data )
	{

	}

	// 소멸자
	virtual ~User() { }

	// 복사 생성자
	explicit User( const User& _data )
		: name( _data.name )
	{
		/* Nothing */
	}

	// 복사 대입 연산자 
	const User& operator=( const User& _data )
	{
		name = _data.name;

		return *this;
	}

private :
	std::string name;
	CObject     m_Obj; // 맴버가 아니라고 가정!
	
};


이처럼 최초 User 클래스를 이쁘게 만들어놨다 치자!
추후에 다음 클래스가 맴버 or 상속으로 쓰인다면?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CObject
{
public :
	explicit CObject()
		: nObjectID(0),
		    nRenderType(0)
	{ /* Nothing */ }

	virtual ~CObject() { /* Nothing */ }

public :
	int nObjectID;
	int nRenderType;
};
 


처음 코드에서 맴버 or 상속이 안되있다고 한 가정들을 하나씩 풀어서 생각해보자.


1
2
3
4
5
6
7
8
9
10
11
12
void main()
{
        User pc1;

 	 pc1.nObjectID   = 999;
 	 pc1.nRenderType = 1;

	User pc2(pc1); // 복사 생성자 호출! CObject 값이 복사 되지 않음

	User pc3;
	pc3 = pc1;      // 복사 대입 연산자 호출! CObject 값이 복사 되지 않음	
}
 

맴버 or 상속 어떤 경우라도 애초에 의도한 대로 모든 데이터가 복사 되지 않는다. 


편리한 해결법은 없다. 경우에 맞게 빠뜨리지 말고 '복사 생성자' 와 '복사 대입 연산자'에 추가된 것들 모두 추가해주자.

클래스를 설계할 때 초기화 및 복사 해주는 함수를 두면 신경을 좀 덜쓰게 해줄 수 있다.
파생 클래스 이든 가져다 쓰는 클래스이든 사용되어지는 클래스에 초기화 및 복사관련 도우미 함수가 있다면
가져다 쓰는 사용자는 아! 초기화 or 복사 함수를 수정해야겠구나! 라고 생각나게 해주는 정도??? ㅋㅋㅋ
직접 추가해야 되는건 매한가지이다 ㅋ



■ 책 요약

객체 복사 함수는 주어진 객체의 모든 데이터 맴버 및 모든 기본 클래스 부분을 빠뜨리지 말고 복사해야 합니다.

클래스의 복사 함수 두 개를 구현할 때, 한쪽을 이용해서 다른 쪽을 구현하려는 시도는 절대로 하지 마세요.
그 대신, 공통된 동작을 제 3의 함수에다 분리해 놓고 양쪽에서 이것을 호출하게 만들어서 해결합시다.
  

 

Posted by 닭꽝
비공개2011. 7. 7. 16:27

항목 1. C++를 언어들의 연합체로 바라보는 안목은 필수

C++ 이란?   상관 관계가 있는 여러 언어들의 연합체( federation)
                     C++를 구성하는 요소들은 각각의 규칙을 가지고 있다. 

■ C++ 구성요소

1) C 

2) 객체 지향 개념의 C++ : 클래스, 캡슐화, 상속, 다형성, 가상 함수( 동적 바인딩 ) 등

3) 템플릿 C++ : 새로운 프로그래밍 패러다임 파생 -> 템플릿 메타프로그래밍( TMP )

4) STL
템플릿 라이브러리( 컨테이너 / 반복자 / 알고리즘 / 함수 객체 ... )



 

 

항목 2. #define을 쓰려거든 cosnt, enum, inline을 떠올리자

            부제 : 가급적 선행처리자( define )보다 컴파일러를 더 가까이 하자
 

■ define 키워드 단점

1) define 관련 컴파일 에러시 에러메세지 내용이 직관적이지 못하다.  
 

1
2
3
4
5
6
/*
다음 define 관련하여 컴파일 오류가 생길 경우
에러 메세지에서 'MAX_COUNT' 어쩌구는 거론되지않고 
'100'이라는 숫자만 거론됨. ;;;
*/
#define MAX_COUNT 100
   

■ define을 상수( const )로 교체하라 

1) 부동소수점을 쓸 경우에는 define을 썻을 때보다 최종코드의 크기가 작아 질 수 있다.

2) 기호 테이블에 들어가며 컴파일 문제 발생시 정확한 위치를 직관적으로 표시해준다.


■ define -> const 변경시 유의점

1) 상수 포인터를 정의하는 경우    
 

1
2
3
4
5
6
7
8
9
/*
포인터와 가르키는 대상까지 const로 선언해주자.
*/
const char * const authorName = "Scott Meyers";

/*
char* 가 싫다면 string으로 대체
*/
const std::string authorName("SocttMeyers");
 


2) 클래스 맴버로 상수를 정의하는 경우


1
2
3
4
5
6
7
8
9
10
// .h file
class CostEstimate
{
private :
	static const double FudgeFactor;
	
};

// .cpp file
const double CostEstimate::FudgeFactor = 1.35;

 
■ 클래스 내에서 상수가 필요할때는 enum 이 최고!

1) const보다 #define에 가깝다. int 자료형으로 취급되며 상수값만 유효하다.

2) 메모리에 할당 되지 않는다.

3) 클래스내에서의 enum은 다양한 설계에 유용하게 활용된다.
    템플릿 메타프로그래밍의 핵심 기법이기도 하다.

■ 함수처럼 쓰이는 매크로로 만들고자 한다면 #define 매크로 보다 inline 함수를 우선 생각하자.

  1) #define 매크로 잘못 사용 한 예

1
2
3
4
5
6
7
#define CALL_WITH_MAX(a,b) ( (a)>(b) ? (a):(b) ) // 매크로 전체를 ()로 감싸지 않으면 책임못짐;;

int a = 5;
int b = 0;

CALL_WITH_MAX( ++a, b );     // a는 2번 증가! 뭥미;;;
CALL_WITH_MAX( ++a, b+10 ); // a는 1번 증가! 
 

2) inline 함수 템플릿

1
2
3
4
5
template< typename T >
inline int callwithMax( const T& a, const T& b )
{
	return ( a > b ? a : b );
}
 





 

항목 3. 낌새만 보이면 const를 들이대 보자!

■ const 의미 해석법

const가 *표의 왼쪽    : 포인터가 가르키는 대상이 상수
const가 *표의 오른쪽 : 포인터 자체가 상수


■ const의 효과 예시
 

1
2
3
Temp a, b, c;

if( a * b = c ) // 으악… 나는 비교( a * b == c ) 하려 했었는데… ㅠㅠ


const Temp Operate* ( const Temp& rhs ); 처럼
const 를 사용하면 위와 같은 실수를 미연에 방지하여 error 메시지를 발생시킨다.

 



 

항목 4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자


■ 대입과 초기화를 헷갈리지 말자

 

1
2
3
4
5
6
7
8
9
10
// [대입] 기본 생성자 호출 -> 대입 연산자 호출!
Test::Test( const std::string& name, 
			const std::string& address, 
			const std::list<PhoneNumber>& phones )
{
	theName		= name;
	theAddress	= address;
	thePhones	= phones;
	nCount		= 0;
}

- 맴버 초기화 리스트를 사용 한 것보다 비용이 큼

- C++ 규칙에에 의하면 어떤 객체이든 그 객체의 데이터 맴버는
   생성자의 본문이 실행되기 전에 초기화 되어야 한다고 명기 되어있다.
   이방법을 '초기화'라고 생각하지 말자.


 
1
2
3
4
5
6
7
8
9
10
11
// [초기화] 복사 생성자 한번 호출
Test::Test( const std::string& name, 
			const std::string& address, 
			const std::list<PhoneNumber>& phones )
	: theName(name),
	  theAddress(address),
	  thePhones(phones),
	  nCount(0)
{
	/* Nothing */
}


- 자신의 코딩 정책으로 '맴버 초기화 리스트'를 적용하자. ( 습관화 / 의무화 )

- 만약에 나열된 초기화할 객체가 많아 길어지는게 마음에 걸린다면 ???
   
대입으로도 초기화가 가능한 데이터 맴버들을 빼내어 별도의 함수( ex. init() )로 옮기는 것도 방법이다.


초기화 순서

 

1) 기본 클래스는 파생 클래스보다 먼저 초기화 된다.

2) 클래스 데이터 맴버는 그들이 선언된 순서대로 초기화된다.

 

가급적 선언된 파라메타 순서 그대로 초기화 해주는 습관을 기르자
 

 

정적 객체 ( static object )


extern / static 키워드를 사용하여 선언한 객체!!
 

- 종류

1) 전역 객체

2) 네임스페이스 유효범위에서 정의된 객체

3) 클래스 안에서 static으로 선언된 객체

4) 함수 안에서 static으로 선언된 객체( 지역 전역 객체 )

5) 유효범위에서 static으로 선언된 객체

 

 

- 문제가 발생 할 수 있는 부분

별개의 번역 단위에서 정의된 비지역 정적 객체들의 초기화 순서는 정해져 있지 않다

 

- 해결책( Singleton pattern )

함수를 두어 최초 호출시 객체를 생성하고 그 이후로는 참조자만 반환해주자. 

1
2
3
4
5
6
7
class FileSystem { ... };

FilesSystem& tfs()
{
	static FileSystem fs;
	return fs;
}
 

비지역 정적 객체 -> 지역 정적 객체로 교채!!! 
   
Posted by 닭꽝
비공개2011. 7. 7. 16:23

정답 or 바이블이 아닌 지침/안내서
자주 지나치는 C++ 프로그래밍의 이모저모를 재조명하는 것
책의 범위 : 표준(standard) C++까지로 한정. 
 
 

- 이 책에서 다룬 설계 이슈

   C++로 어떤 일을 해야 할 때 취할 수 있는 서로 다른 접근들 사이에서의 선택방법에 초점

- 한번쯤 고민하거나 놓치는 것들
 

1.  상속과 템플릿 사이에서 선택
 

2.  Public 상속과 private 상속 사이에서의 선택
 

3.  Private 상속과 클래스 합성 사이에서의 선택
 

4.  멤버함수와 비 멤버 함수 사이에서의 선택
 

5.  파라메타 전달 시 레퍼런스와 포인터 사이에서의 선택
 

6.  대입연산자는 어떤 타입을 반환해야 적당할까
 

7.  소멸자를 가상 함수로 만들어야 할 때는 언제일까
 

8.  충분한 메모리를 찾지 못했을때 operator new는 어떻게 동작시켜야 할까


 

Posted by 닭꽝
비공개2011. 7. 7. 11:00
표준 템플릿 라이브러리( Standard Template Library: STL )

    주요 구성요소서 컨테이너( vector, string, map 등 )
                           반복자
                           알고리즘( find, sort, transform 등 )
                           함수 객체( less, greater 등 )
     외에 이런저런 컨테이너 어댑터와 함수 객체 어댑터( stack, priority_queue, mem_fun, not1 등 ) 가 있습니다.

iostream

    사용자 정의 버퍼링, 국제화 기능이 가능한 입출력을 지원하며,
    그 외에 cin, cout, cerr, clog 등의 사전정의 객체를 지원합니다.

국제화 지원

    여러 로케일( locale )을 활성화시킬 수 있는 기능이 포함되어 있습니다. 또한 wchar_t등의 타입( 대개 16비트/문자 ) 및         wstring( wchar_t 타입으로 정의한 string )을 쓰면 유니코드를 사용할 수 있습니다.

수치 처리 지원

    복소수를 나타내는 템플릿( complex ) 및 수치 배열을 나타내는 템플릿( valarray ) 이 여기에 해당됩니다.

예외 클래스 계통

    최상위 클래스인 exception 및 이것으로부터 갈라져 나온 파생 클래스들,
    예를 들어 logic_error 및 runtime_error 등이 여기에 포함됩니다.

C89의 표준 라이브러리

    1989년 버전의 C에 포함된 표준 라이브러리는 전부 C++에도 들어 있습니다. 

발췌 ) Effective C++ - 스콧 마이어스 

'비공개' 카테고리의 다른 글

[ Effective C++ ] Item 1. C++에 왔으면 C++의 법을 따릅시다  (0) 2011.07.07
[ Effective C++ ] Intro  (0) 2011.07.07
TcMalloc lib 적용 시도!!!  (0) 2011.06.27
C++ 0x ( visual studio 2010 적용 )  (0) 2011.06.17
CreateFile  (0) 2011.05.12
Posted by 닭꽝
비공개2011. 6. 27. 17:19
간단히 TcMalloc lib.에 대하여 소개를 해보자면!( 다음 정보는 펌! 출처는... ㄷㄷ )

1) 작동 방식(성능 향상 방식)
    - 중앙 메모리 관리자와 쓰레드별 메모리 관리자를 구분하고 작은 크기(32K 이하)의 메모리 할당/해제 요청은 
      쓰레드별 메모리 관리자가 처리하고, 부족할 경우 중앙 메모리 관리자에서 얻어오는 방식으로 처리함.
    - 큰 메모리(32K 이상)는 전역 관리자에서 페이지 크기(4K) 단위로 클래스를 나누어 mmap()을 이용하여 할당 함.
 
2) 지원 환경
    - Linux (32 and 64 bit), Mac OSX, Windows (32 bit only), Solaris
    - NUMA-aware TCMalloc(NUMA아키텍처 지원됨)
 
3) 사용 사례
    - 구글 크롬, Webkit, MySQL, HyperTable, Memcached, Redis, Nginx

ref. keyword ) jemalloc / nedmalloc / ptmalloc



요놈을 내가 맡은 프로젝트에 적용해보려고 하였다.
참고로 적용시키려하는 프로젝트는 다년간 서비스 되어온 상용게임의 클라이언트이다.


1. library는 어디서 구하나? 그리고, 어떻게 적용시키나?

어디? : http://code.google.com/p/google-perftools/downloads/list
위 URL 내에서 Google Perftools 최신 버전을 받는다. 내가 받은건  google-perftools-1.7.zip !!!

어떻게?  
  1) perftools 솔루션 내의 'libtcmalloc_minimal' 프로젝트를 빌드하면 다음 결과물 생성.
  결과물 : nedmalloc_ptchg_v110.dll   /   nedmalloc_ptchg_v110.lib

  2) 적용하고자 하는 프로젝트 링커>>출력 파일에  dll 추가.( lib 파일이 프로젝트 폴더내에 필요함 )
  
  3) #include "tcmalloc.h"

  4) tc 함수 사용!!! 

2. 적용 시키기전에 테스트는 어떻게 하나?

새로운 프로젝트를 만들어서 다음 항목으로 동적할당 테스트를 수행 함.

1) malloc
2) new
3) tc_malloc
4) tc_newarray
5) tc_new

수행방법. 
  가. 4(byte) ~ 131,072(byte) 16단계로 나누어 수행
  나. '가' byte 단위로 각각 10,000 / 100,000 / 1,000,000 / 10,000,000 / 100,000,000  씩 할당/해제 반복!
              

3. 적용은 잘 되는가?

테스트 프로젝트에서는 적용 잘 됨.

4. 작업을 수행하기 위해서는 비용이 얼마나 드는가?

처음에는 전역으로 new 연산자를 오버로딩 해보았으나. link error 주구장창!!! ㅜ.ㅡ
게임 클라이언트 자체적으로 큰 프로젝트이기에 어디에다가 new 연산자를 넣어야 링크 에러가 안날까 
고민하다가 보류!!

그래서, 클래스 내부적으로 new 연산자 오버로딩 시켜서 테스트 진행함.( ex. 케릭터 클래스, NPC 클래스 ... )

실제 적용하기 위해선 그 수백-수천개의 클래스에 일일이 오버로딩 해야할 feel;;;

고로, 지금 상태에선 비용은 '삽질'과 '노가다'가 조화를 이룰것으로 추정!

뭐, 실제 적용하려고 한다면 또 깊게 고민하겠지만, '그냥 테스트 해보기' 이기에 진지하게 고민 안 함.

5. 실제 클라이언트에 적용 후 문제점은 없는가?

테스트 프로젝트를 만들기전에, 실제 게임 클라이언트에 부분 적용해 보았다.

적용 방법은 new 연산자를 오버로딩하였다.
 
컴파일등은 잘 되었는데, 게임 진행중 클라이언트가 죽는 현상 발생! 
정확한 원인은 기억이 안난다;;; 메모리 침범이였던거같다 ㅎㅎ

생각보다 쉽게 적용이 안되서 절망 ㅜ.ㅡ

6. 결론적으로 tcmalloc allocator를 적용하면 성능 향상이 되는가?

시간 측정 결과 매우 많이 비관적이게 나왔지만, 
나의 테스트 환경이 최적화 되었다고 볼 수 없기에 다음과 같이 결론 내린다.

많은 스레드를 사용하지 않는 게임 클라이언트에서는 큰 효과를 보기 어렵다.!!!
 
7. 생각해볼 문제
 
1) 실제 프로젝트 전체적으로 적용시켜보지 못해봐서 아쉽다.
2) 클라이언트에서 실질적으로 동정할당이 얼마나 일어나는가?
3) 발생하는 동적할당의 평균적인 byte는 얼마가 되는가?
4) 안정성 검증은 어떻게 테스트 해보아야 하는가? 
5) 테스트 방법이 적절한가? 
6) 테스트 환경이 동등한가?

'비공개' 카테고리의 다른 글

[ Effective C++ ] Intro  (0) 2011.07.07
표준 C++ 라이브러리의 주요 구성요소( C++98 에 명시 )  (0) 2011.07.07
C++ 0x ( visual studio 2010 적용 )  (0) 2011.06.17
CreateFile  (0) 2011.05.12
GetLastError() 활용  (0) 2011.05.12
Posted by 닭꽝
비공개2011. 6. 17. 16:50

C++ 0x란 : http://blog.naver.com/sorkelf?Redirect=Log&logNo=40126584872

- auto

- static_assert

- Rvalue Reference

- lambda 

기능 설명 : http://yamoe.tistory.com/226


 ■ 동시성 런타임 MSDN

http://msdn.microsoft.com/ko-kr/library/dd504870(vs.100).aspx

 ■ Visual C++ Team 블로그

 ■ VSTS 2010 스터디 블로그


 ■ Parallel Programming in vative code


 ■ 발표자 blog

Posted by 닭꽝
비공개2011. 5. 12. 11:39


 먼저, 이 함수를 다시 보게 되는 이유는! 내가 몸 담고 있는 프로젝트에서 발생한 일 때문이다.
 PC 내에서 응용프로그램을 두개 실행시켰을 때!!
 특정 상황에서 동시 파일 접근시 문제가 발생 하였다.

 FILE_SHARE_WRITE -> FILE_SHARE_READ 로 문제 해결!


파일을 생성하거나 기존파일이나 I/O 디바이스를 열기 위한 win32표준 함수

I/0 디바이스 : file, file stream, directory, physical disk, volume, console buffer, 
                    tape drive, communications resource, mailslot, pipe

 
HANDLE WINAPI CreateFile(
            LPCTSTR lpFileName,
            DWORD dwDesiredAccess,
            DWORD dwShareMode,
            LPSECURITY_ATTRIBUTES lpSecurityAttributes,
            DWORD dwCreationDisposition,
            DWORD dwFlagsAndAttributes,
            HANDLE hTemplateFile
);


lpFileName
 ┣> 열거나 읽을 파일이름
 ┣> 파일이름은 전체 경로를 줘야 함(최대 길이 :  MAX_PATH = 260 바이트)
 ┣> 파일 길이가 초과 할 경우 유니코드 버전 호출 후 "
\\?\"를 덧붙여 주면 됨
 ┃  (단, win CE에서는 "
\\?\" 문자열을 지원하지 않음)
 ┗> 네트워크 서버 참조 할 때는  "\\<서버 name>\<공유 name>" 형식으로 사용

===================================================================================

dwDesiredAccess
 ┣> 장치나 파일을 어떤 모드로 액세스 할 지 정해주는 부분
 ┣> GENERIC_READ(읽기 권한), GENERIC_WRITE(쓰기 권한)
 ┗> 0을 이용하여 읽기나 쓰기를 사용하지 않거나 OR을 이용하여 중복사용 가능

===================================================================================

dwShareMode
 ┣> 파일을 어떤 특성으로 열것인가를 설정하는 부분(읽기, 쓰기)
 ┗> FILE_SHARE_READ(파일 읽기만 가능), FILE_SHARE_WRITE(파일 쓰기만 가능)

===================================================================================

lpSecurityAttributes
 ┣> 리턴된 핸들을 차일드 프로세스로 상속할 것인지 아닌지를 지정하는 SECURITY_ATTRIBUTES 구조체 포인터
 ┣> NULL을 사용할 경우 사용하지 않음
 ┗> 
WIN CE에서는 lpSecurityAttributes 파라미터를 무시하므로 무조건 NULL

===================================================================================

dwCreationDisposition
 ┣> 파일을 어떻게 열고 생성을 할 것은지에 대해서 설정
 ┃
 ┣> CREATE_NEW
 ┃            ┗ 새로운 파일 생성, 이미 파일이 존재하면 실패
 ┣> CREATE_ALWAYS
 ┃            ┗ 새로운 파일을 생성하거나, 파일이 있을 시 파일을 덮어씀(크기: 0BYTE)
 ┣> OPEN_EXISTING
 ┃            ┗ 기존 파일이 있으면 파일을 열고, 없으면 에러 코드 발생
 ┣> OPEN_ALWAYS
 ┃            ┗ 파일이 존재하지 않으면 생성하고, 파일이 있으면 기존파일을 연다
 ┗> TRUNCATE_EXISTING
               ┗ 기존파일을 열고 기존파일을 0 BYTE로 초기화 시킴
                   GENERIC_WRITE로 파일을 열어야 이 플래그 사용가능

===================================================================================

dwFlagsAndAttributes
 ┣> 파일의 속성을 설정
 ┃
 ┃
 ┣> FILE_ATTRIBUTE_ARCHIVE
 ┃            ┗ 파일의 아카이브비트(파일의 백업 여부) 설정
 ┃
 ┣> FILE_ATTRIBUTE_ENCRYPTED
 ┃            ┗ 파일이나 디렉토리가 암호화 속성을 가짐
 ┃                파일은 데이터가 암호화되고 
 ┃                디렉토리는, 디렉토리안에 새롭게 작성되는 파일이나 서브 디렉토리가 
 ┃                디폴트로 암호화 되며, FILE_ATTRIBUTE_SYSTEM 지정이 되면 적용안됨
 ┃
 ┣> FILE_ATTRIBUTE_HIDDEN
 ┃            ┗ 파일의 숨김 비트 설정
 ┃
 ┣> FILE_ATTRIBUTE_NORMAL
 ┃            ┗ 아무 속성도 가지지 않는 파일, 단독으로 쓰여야 함
 ┃
 ┣> FILE_ATTRIBUTE_OFFLINE
 ┃            ┗ 연결되지 않은 저장 장치에 있어 즉시 사용할 수 없는 파일
 ┃
 ┣> FILE_ATTRIBUTE_READONLY
 ┃            ┗ 파일을 읽기 전용 속성 비트 설정
 ┃
 ┣> FILE_ATTRIBUTE_SYSTEM
 ┃            ┗ 파일이 중요한 시스템 파일임을 나타내는 시스템 비트 설정
 ┃
 ┣> FILE_ATTRIBUTE_TEMPORARY
 ┃            ┗ 임시 저장소에 저장되는 파일, 디스크에 저장이 되지 않고 
 ┃                메모리에 저장되어 연결 속도가 빠르며 사용 후 반드시 삭제해야 함
 ┃
 ┃
 ┃
 ┣> FILE_FLAG_BACKUP_SEMANTICS
 ┃            ┗ 백업이나 복원을 위해 파일이 열리거나 생성 
 ┃
 ┣> FILE_FLAG_DELETE_ON_CLOSE
 ┃            ┗ 핸들이 닫힌 후 파일 삭제
 ┃
 ┣> FILE_FLAG_NO_BUFFERING
 ┃            ┗ 파일이나 디바이스를 데이터의 읽기와 쓰기의 시스템 캐싱 없이 연다
 ┃
 ┣> FILE_FLAG_OPEN_NO_RECALL
 ┃            ┗ 원격 저장 장치에서 사용하는 플래그
 ┃
 ┣> FILE_FLAG_OPEN_REPARSE_POINT
 ┃            ┗ reparse point를 작동시키지 않고 파일을 염
 ┃
 ┣> FILE_FLAG_OVERLAPPED
 ┃            ┗ 파일이나 디바이스 비동기 I/O로 열거나 생성함
 ┃
 ┣> FILE_FLAG_POSIX_SEMANTICS
 ┃            ┗ POSIX 규칙대로 파일을 액세스 함
 ┃                16Bit 윈도우나 MS-DOS에서는 사용할 수 없음
 ┃
 ┣> FILE_FLAG_RANDOM_ACCESS
 ┃            ┗ 임의의 접근 파일임을 나타냄
 ┃
 ┣> FILE_FLAG_SEQUENTIAL_SCAN
 ┃            ┗ 순차적 접근 파일임을 나타냄
 ┃
 ┣> FILE_FLAG_WRITE_THROUGH
 ┃            ┗ 데이터 출력시 캐시를 통하지 않고 곧바로 디스크로 출력
 ┃
 ┃
 ┗> 
단, WINCE에서는 
         FILE_ATTRIBUTE_OFFLINE, FILE_FLAG_OVERLAPPED, FILE_FLAG_NO_BUFFERING, 
         FILE_FLAG_SEQUENTIAL_SCAN, FILE_FLAG_DELETE_ON_CLOSE,
         FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_POSIX_SEMANTICS등을 지원하지 않음
         FILE_ATTRIBUTE_TEMPORARY 플래그는 분리된 드라이브나 네트워크 공유 디렉토리를 나타낼때 사용

===================================================================================

hTemplateFile
 ┣> 새로만들고자 하는 파일의 추가 속성을 지원하는 템플릿 파일의 핸들 지정
 ┣> 통상 사용되지 않아서 NULL로 지정하는 게 보통
 ┗>
 WINCE에서는 무시되므로 NULL 설정



참조 : http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
         윈도우즈 API 정복(김상형 저)
         윈도우 임베디드 CE6.0 프로그래밍(더글라스 볼링 저)












'비공개' 카테고리의 다른 글

TcMalloc lib 적용 시도!!!  (0) 2011.06.27
C++ 0x ( visual studio 2010 적용 )  (0) 2011.06.17
GetLastError() 활용  (0) 2011.05.12
BMW, ‘믿기힘든 연비 운전 기술’ 7가지 공개  (0) 2011.05.09
구조체 와 메모리  (0) 2011.04.27
Posted by 닭꽝
비공개2011. 5. 12. 11:35

에러 코드에 대한 내용을 일일이 찾는건 번거롭다.

visual studio 6.0 을 설치하면

설치되는 경로에 Error LookUp 이라는 응용프로그램이 있다.

코드값을 넣어주면 한글로 그 값에 대한 내용을 바로 보여준다.

얼마나 편한 세상인가? ㅎㅎ



그리고 디버그의 Watch창에 $err,[변수명] 을 입력하면 에러 내용을 확인할 수 있다.

WinError.h에 정의되어 있는 오류 메세지도 확인 할 수 있따.

아참! SetLastError( DWORD _dwValue )를 통해 사용자 임의로 에러코드를 지정 할 수도 있다.

( 참고로, 게임 개발 및 유지보수 시에는 client 자체적으로 Log 파일( Log Interface 개발 )을 만들어 발생하는 

오류에 대해 기록 및 활용하면 좋다. )

'비공개' 카테고리의 다른 글

C++ 0x ( visual studio 2010 적용 )  (0) 2011.06.17
CreateFile  (0) 2011.05.12
BMW, ‘믿기힘든 연비 운전 기술’ 7가지 공개  (0) 2011.05.09
구조체 와 메모리  (0) 2011.04.27
심층 면접 법  (0) 2011.04.04
Posted by 닭꽝
비공개2011. 5. 9. 09:52

1. 워밍업을 하지 말고 바로 출발할 것

- 최신 차종들은 워밍업이 불필요하다.

2. 멀리 내다보고 균형있게 운전할 것(교통신호와 교통의 흐름을 잘 살펴볼 것)

- 속도를 얼마나 줄여야 할지에 따라 가속페달을 일찍 떼거나 혹은 기어를 중립으로 옮기는 방식을 선택할 것.

3. 온보드컴퓨터를 통해 순간연비와 변속 시점을 계속 살펴볼 것.

4. 가속페달에서 발을 보다 일찍 뗄 것

- 브레이크를 밟는 대신 퓨얼컷을 이용해서 감속할 것.

5. 신호대기나 교통정체시 시동을 끌 것

- 스타트앤스톱 기능을 활용.
   최신 자동차들은 재시동에 그리 많은 연료를 소비하지 않기 때문에
   시동을 수시로 끄는 것이 오히려 바람직하다는 결론


6. 높은 기어를 넣고 강하게 가속할것

- 가속할 때 평소보다 더 높은 기어를 이용하고 가속페달은 2/3 가량만 밟을 것

7. 50km/h에서 100km/h로 가속할 때 3단기어보다 5단 기어를 이용하면 연비 10%가 향상된다.

'비공개' 카테고리의 다른 글

CreateFile  (0) 2011.05.12
GetLastError() 활용  (0) 2011.05.12
구조체 와 메모리  (0) 2011.04.27
심층 면접 법  (0) 2011.04.04
클라이딩 컴퓨팅 & 서비스  (0) 2011.02.16
Posted by 닭꽝
비공개2011. 4. 27. 11:37

struct _tagTEST
{
short  A;         // 2byte
int      B[10];   // 40byte
}

32bit 운영체제는 최적화를 위해 4byte 단위로 메모리를 관리한다.

그러므로, A와 B는 순차적으로 메모리상에 할당되지 않고,

2byte(A)+ 2byte(Temp) + 40byte(B array) 존재하게 된다.

_tagTEST 구조체의 정확한 바이트수를 안다하더라도 memcpy 등 메모리 상의 있는 값을 그대로 덮을 경우

의도하지 않는 문제가 발생 할 수 있다.

해결방법은!

1. short 를 int로 하여 4byte단위로 구성한다.
2. 구조체 내에서 byte 수가 가장 큰 맴버를 가장 위에 배치한다. 변수A -> 배열B (X) / 배열B -> 변수A (O)

다른 방법은 또 뭐가 있을까?

'비공개' 카테고리의 다른 글

GetLastError() 활용  (0) 2011.05.12
BMW, ‘믿기힘든 연비 운전 기술’ 7가지 공개  (0) 2011.05.09
심층 면접 법  (0) 2011.04.04
클라이딩 컴퓨팅 & 서비스  (0) 2011.02.16
카드가 말하지 않는 23가지  (0) 2011.01.27
Posted by 닭꽝