
일시: 2009년 10월 18일 (일요일) 오후 2시 30분
장소: 강남웨딩홀 (구 마샬 웨딩프라자) 3층 그레이스홀
제가 이런 글을 올리게 될 줄은 저도 몰랐습니다만, 어찌어찌하여 장가를 가게 되었습니다.
많이들 오셔서 자리를 빛내주시면 대단히 감사하겠습니다. :)

Before
//////////////////////////////////////////////////////////////////////////////////////////
/// \brief 글자가
/// 처음으로
/// 나타나는 위치를
/// 반환한다.
/// \param caseSensitive 대소문자 구별을 하는가?
/// \return size_t 음 이것은 그냥 붙여보는 주석이란다. abcdefg hijklmn opqrstu vwxyz 음 이것은 그냥 붙여보는 주석이란다. abcdefg hijklmn opqrstu vwxyz
//////////////////////////////////////////////////////////////////////////////////////////
음 "그냥 붙여보는 주석이란다..." 부분이 원래 한 줄인데, 워드랩되어버리는구나. 어쨌든 원래는 매우 긴 라인이다.
After
//////////////////////////////////////////////////////////////////////////////////////////
/// \brief 글자가 처음으로 나타나는 위치를 반환한다.
/// \param text 검색 대상 문자열
/// \return size_t 음 이것은 그냥 붙여보는 주석이란다. abcdefg hijklmn opqrstu vwxyz 음
/// 이것은 그냥 붙여보는 주석이란다. abcdefg hijklmn opqrstu vwxyz
//////////////////////////////////////////////////////////////////////////////////////////
C 문자열 주위를 _T() 매크로로 감싸주는 Visual Studio 매크로다.
매크로 IDE(ALT+F11)에서 붙여넣고, 단축키 등록해 준 다음 쓰면 된다. 문자열을 선택한 다음 실행해도 되고, 문자열 가운데 아무데서나 그냥 실행해도 된다.
"고유 ID 생성"이라는 문제는 서버 프로그래밍을 하다보면 빈번히 마주치는 문제다.
전형적인 문제 중에 하나로서 게임 서버에서 플레이어가 사용할 아이템을 생성하는 경우가 있다. 아이템에 대한 정보를 데이터베이스에 집어넣을 때는 고유 ID가 필요하다. 물론 고유 ID 없이 집어넣을 수도 있겠지만, 운영 상의 문제 등을 위해 집어넣는 것이 좋다.
고유 ID를 사용해야 하는 객체, 즉 게임 서버가 하나라면 별 문제가 없다. 그냥 프로그램 내부에서 변수 하나 선언하고, 필요할 때마다 1씩 증가시켜가며 사용하면 되니까. 하지만 게임 서버가 여러 대라면 좀 귀찮아진다.
이런 경우, 일반적으로 가장 먼저 나오는 방법이 데이터베이스 상에서 제공하는 auto increment 기능을 이용하는 방법이다. SQL Server 같은 경우에는 @@IDENTITY 값을 이용하면, 하나의 행을 추가할 때마다 고유한 ID를 얻을 수 있다.
@@IDENTITY 값은 여러 가지 제약 사항이 있기 때문에, 데이터베이스를 이용하지 않고, ID 발급을 위한 중앙 서버를 따로 둘 수도 있다. 이에 관한 건 GPG 6권 7.3 장에 잘 소개되어 있다.
하지만 데이터베이스 또는 ID 발급 서버 같이 중앙 저장소가 따로 존재하는 경우, 데이터 생성 시점에서 고유 ID를 바로 알 수 없다는 문제점이 있다. 데이터베이스는 INSERT 후에야 고유 ID 값을 알 수 있고, 중앙 서버를 두는 경우에는 패킷이 한번 왔다갔다해야 고유 ID 값을 알 수 있다는 말이다. 이 처리가 은근히 귀찮다. 로직이 비동기 방식이 되기 때문이다.
이 문제를 해결하는 의외로 간단하다. 각각의 게임 서버에서 고유 ID 값을 1씩 증가시키는 것이 아니라 게임 서버의 갯수만큼씩 증가시키면 된다.
class base
{
public:
void nvf(); // 1 <----+
virtual void vf(); // 2 |
... // |
}; // |
// |
class derived : public base // |
{ // |
public: // |
void nvf(); // 3 |
virtual void vf(); // 4 <----|--+
... // | |
}; // | |
// | |
base* ptr = new derived; // | |
ptr->nvf(); // 5 -----+ |
ptr->vf(); // 6 --------+
delete ptr; // 7
5번의 경우에는 1번 함수가 호출될 것이고, 6번의 경우에는 4번 함수가 호출될 것이다.
소멸자도 이와 다를 게 없다. 7번에서 소멸자가 virtual 이라면 derived::~derived()가 호출되고, virtual이 아니라면 base::~base()가 호출된다. (자식 클래스에서 소멸자 호출시, 부모 클래스의 소멸자가 연달아 호출된다는 점은 넘어가자.)
즉 위와 같이 derived 클래스를 base 포인터에다 할당하고 삭제하는 경우에는 소멸자를 virtual로 해줘야 한다. 그렇지 않으면 ~derived() 함수가 호출되지 않아, 이 안에 있는 리소스(?) 정리 코드가 호출되지 않기 때문이다. 즉 메모리 릭이 발생할 수 있다. 하지만 아래와 같은 경우에는 굳이 소멸자를 virtual로 만들 필요가 없다.
포인터가 derived* 타입이므로, 소멸자가 virtual이든 아니든 derived::~derived() 함수가 호출되기 때문이다.
즉 소멸자를 virtual로 선언해야 하는지를 판단하기 위해서는, 해당 클래스를 상속받는 클래스가 있느냐/없느냐를 기준으로 삼을 것이 아니라, 실제로 사용할 때 베이스 클래스 포인터를 이용해 접근하는가/아닌가를 기준으로 삼아야 한다.
다만 안타깝게도, 누군가가 작성한 클래스를 다른 사람이 사용한다고 했을 때, 그 사람이 base 포인터를 이용해 작업을 할지, derived 포인터를 이용해서 작업을 할지 최초 작성자는 알 수 없다. 결국 안전빵(?)으로 가기 위해 소멸자는 무조건 virtual로 선언하는 경우가 많다.
일반적인 PC 환경에서 virtual 소멸자 마구 쓴다고 해서 딱히 크게 문제될 것은 없다. 인스턴스당 4바이트 정도의 메모리가 더 들어가고, vtable 또한 어딘가에 생성되겠지만, 시대가 어떤 시댄데...
다만 C 구조체를 상속받아서 그 구조체를 인자로 받는 함수로 전달하거나, 객체를 통채로 직렬화하려는 경우에는 주의해야 한다.
댓글을 달아 주세요
댓글 RSS 주소 : http://serious-code.net/tc/rss/comment/15댓글 ATOM 주소 : http://serious-code.net/tc/atom/comment/15
축하축하. 결혼식 전에 한번 더 포스팅해주삼-ㅅ-;
웃흥.. 추카 추카.. 어솨염 유부 월드~
축하드려요.
위키 첫페이지가 허전해진 이유가 있었군요.
준비 잘 하세요~
축하... 난 e-청첩장이라도 있는 줄 알았다.
목욜날 보자꾸..
감사합니다. 다들 조만간 연락 드리겠습니다. :)
음?
추카추카~~~!!!! 이럴수가;; 깜짝 놀랐어요-_-;
정말 축하드려요 ^^ 캐나다라서 참석은 못할듯 ㅠㅠ 진심으로 축하드려요~~!
sonee// 그려, 고마워. 슬슬 한국 올 때 안 됬나? ㅎㅎ.
우선 결혼 축하하고
나도 결혼생각이 별루없지만.
니가 나보다 빨리 갈줄은 몰랐다.
앞으로 같이 살면 콜라도 맘대로 못먹겠다..ㅋㅋ
아참 혹시 결혼하면 컴에서 정리할 소중한 자료들이 있다면....
나한테 넘겨~
내가 정리해서 소중히 간직해줄게....ㅋ
perpet// 어 다음주에 보자
결혼은청춘의무덤인데...말리기엔늦었네..
우짜든지행복혀라..
진짜 말도 안되게 우연하게 홈페이지에 들르게 되었는데
결혼하셨군요. 그것도 간발의 차이로..
축하 드립니다. 지금 쯤이면 아직 신혼여행 중 이시려나요?
다시 한번 축하드리고~ 행복하세요~ 'ㅂ')/
신현우// 예, 열심히 살겠습니다. :)