2012년 2월 23일 목요일

[C++] 템플릿 (정리중)

템플릿은 헤더에 모든 코드가 들어있어야한다.
즉, 일반적인 파일분할처럼 .h와 .cpp로 정의와 구현이 구분되어 있으면 안된다는것.
그 이유는 컴파일시 inline함수처럼 코드복사가 일어나기 때문이다.


템플릿 코드를 컴파일해서 오브젝트 파일을 메모장으로 열어보면, 실제로는 TestClass<INT> 또는 TestFunc<FLOAT> 과 같은 이름으로 사용되고 있음을 볼 수 있다.




템플릿 함수 특수화 예시 코드

template <typename T>
void TestFunc(T& a)
{
      .....
}

template <>
void TestFunc(int& a)
{
      .....            ////  int 형에 대해서만 특수하게 적용할 코드를 작성한다.
}

[C++] 상속 (정리중)


자식에서 부모 상속시 헤더 파일에서 인클루드 한다. 이유는??


virtual 함수를 선언하면 클래스에 4바이트 공간이 생기고 그것을 상속클래스에서 관리한다.(함수 포인터 공간인듯) 이로 인해 함수 오버라이딩이 가능해진다.


생성자는 부모부터 실행된다. 초기화리스트(이니셜라이저)로 자식에서 부모 생성자를 호출할 수 있다.
소멸자는 자식부터 실행된다. 메모리 누수 등의 문제를 방지하기 위해서. 이 때문에 소멸자는 반드시 virtual 로 선언해야한다.


부모만이 자식을 품을 수 있다.
부모클래스 포인터로 자식객체를 가리킬 수 있다.(업캐스트)
자식클래스 포인터로 부모객체를 가리킬 수 없다.(다운캐스트: 부모클래스 포인터에서 자식포인터로 형변환)
단, 명시적 형변환은 가능.


상태패턴에 사용된다.

[C++][자료구조] 링크드 리스트(Linked List)

개념 같은건 책을 보면 나와있으니 구현시 실질적으로 중요한 것만 정리.


단일이든 양방향이든 리스트의 처음과 끝에 Head와 Tail 노드를 만든다(포인터가 아니라 실제 공간을 할당받은 노드).
이 두 노드는 데이터를 가지지 않고 처음과 끝을 가리키는 역할만 한다.
(구현시에는 리스트 클래스의 생성자와 소멸자에서 두 노드를 할당, 반환한다.)

이렇게 구현하였을때의 장점은 삽입/삭제 등 함수 내부에서 if 문으로 조건 체크를 해줘야할 빈도수가 현저히 줄어들어 구현이 훨씬 간단해 진다는 점.
사용시에도 조건체크를 하지 않으므로 실행 속도가 빠르다는 점.
단점은 두 개의 공간을 낭비한다는 것 뿐이다.


이것이야말로 일반 대학교재용 자료구조책에서는 절대 찾아 볼 수 없는 실용적인 방법론이라 말하지 아니하지 않을 수 없지 아니하지 않을 수 없는것이다ㅡ,.ㅡ






그리고 이건 햄스터의 슬픔(...응??)



[C++] 문자열 함수 外


size_t strlen ( const char * str );

문자열의 실제 길이를 리턴하는 함수이다. 널문자를 제외한 길이라는 점에 주의하자!!! 참고로 size_t 란건 unsigned int를 재정의한 것이다. 






int strcmp ( const char * str1, const char * str2 );

두개의 문자열을 비교하여 동일할 경우 0이 리턴되고, str1이 클 경우 양수, str2가 클 경우 음수가 리턴된다. 여기서 크다는 것은 문자를 아스키 코드값으로 비교한 결과를 말하는 것이다.






errno_t strcpy_s(
   char *strDestination,
   size_t numberOfElements,
   const char *strSource 
);
문자열을 복사하는 strcpy의 새로운 표준형이다. strcpy의 경우 배열의 첨자범위를 넘어설 위험성이 많으므로 최근의 visual studio에서 사용하면 경고 메시지가 뜬다. 중요한건 두번째 인자가 Dest 버퍼의 크기라는 점만 알면 된다. errno_t는 에러체크를 위해 사용되는 리턴값 같은데 실제 사용할때는 신경안써도 된다.


errno_t strcat_s(
   char *strDestination,
   size_t numberOfElements,
   const char *strSource 
);
복사가 아니라 이어붙이기. 나머지 설명은 위와 동일.









아래는 실제로 문자열을 깊은 복사할때의 코드이다.
가장 주의해서 볼 점은 strlen() 함수는 NULL 문자를 제외한 길이를 리턴한다는 점이다.



char szTemp[256];
cin >> szTemp;


int nLen = strlen(szTemp);


char* m_szName = new char[nLen + 1];


strcpy_s(m_szName, nLen + 1, szTemp);








※ 문자열 함수는 아니지만 콘솔 문자열 입력 관련

cin 객체는 한 개의 문자 단위로 입력받아 처리하며, 공백문자나 개행문자(엔터, '\n')로 tokening 된다.
이때 두 개 이상의 문자를 입력하면 그만큼 예외처리가 여러번 일어나는 것을 볼 수 있다.
이것을 막으려면 입력버퍼를 초기화시키는 fflush(stdin); 명령을 사용하면 편리하다.

cin.getline()  공백문자 포함해서 콘솔에서 문장을 입력받아 저장하는데 사용하는 함수





※ 참조:
http://www.cplusplus.com/
http://msdn.microsoft.com

[C++] 연산자 오버로딩


OperatorOverLoad OperatorOverLoad::operator+( const OperatorOverLoad& rhw)
{
OperatorOverLoad temp;
temp.BindXY(m_nX + rhw.m_nX, m_nY + rhw.m_nY );
return    temp;
}



위는 m_nX와 m_nY 두개의 멤버 변수를 가진 클래스에서의 '+' 연산자 오버로딩의 예시이다.

그런데 사실 이건 중요하지 않고ㅡ,.ㅡ;;

복사생성자 관련 포스트에서도 언급한것 같지만 '=' 연산자 오버로딩이 중요하다.





OperatorOverLoad OperatorOverLoad::operator=( const OperatorOverLoad& rhw)
{
///< 자기 자신 확인 
if( this == &rhw )
{
return *this;
}

///< 확인 
SetName(rhw.m_pName);


return *this;
}




void OperatorOverLoad::SetName(const char* pName)
{
///< 포인터 검사 
if( pName != 0 )
{
int nLen = strlen(pName);
if( nLen != 0 )
{
delete [] m_pName;
m_pName = NULL;


m_pName = new char[nLen + 1];
strcpy_s(m_pName,nLen+1,pName);
}
}
}



위 내용을 요약하자면 char* 형의 멤버변수에 대하여 깊은 복사를 해주고 있다.

char* 형의 멤버변수가 존재할 경우 깊은 복사를 하도록 복사생성자와 '=' 연산자 오버로딩을 둘 다 생성해주어야 한다.

[C++][디자인패턴] static과 싱글톤 패턴

디자인 패턴이란

프로그래머들이 프로젝트를 하면서 자주 사용되는 몇가지 유용한 프로그램 스타일 들에 이름을 붙여서 분류한 것이다. 싱글톤, 팩토리, 옵저버, 커맨드, 상태 패턴 등이 있다. 각 패턴들이 어떤 상황에서 사용되는지를 아는 것이 중요하다.



싱글톤 패턴의 특징

-싱글톤은 매니저 역할을 하는 패턴이다.
-단 하나만 존재하는 전역 클래스이다.
-함수와 변수를 static으로 선언하여 사용한다.




<cStudentMgr.h>


#pragma once


class cStudentMgr
{
private:
static cStudentMgr* m_pInstance;
int m_nID;

public:
static cStudentMgr* GetInstance(void);
void Destory(void);


inline void SetID(int nID)
{
m_nID = nID;
}


inline int GetID(void) const
{
return m_nID;
}


private:
cStudentMgr(void);
~cStudentMgr(void);
};





위 코드를 보면 생성자와 소멸자가 private 영역에 들어가 있다.
이런 경우 클래스의 객체를 만들면 생성자와 소멸자가 private이므로 호출할 수 없다는 에러가 발생한다.
생성자와 소멸자 대신에 GetInstance(), Distroy()를 사용해 프로젝트를 통틀어 단 하나의 객체만을 생성한다.
두 함수의 내부 구현은 이렇다.




<cStudentMgr.cpp>



#include "cStudentMgr.h"


///< 정적 초기화 
cStudentMgr* cStudentMgr::m_pInstance = 0;


cStudentMgr::cStudentMgr(void)
:m_nID(0)
{
}


cStudentMgr::~cStudentMgr(void)
{
}


///< 싱글톤 인스턴스 포인터 얻기 
cStudentMgr* cStudentMgr::GetInstance(void)
{
///< 처음 이라면 
if( m_pInstance == 0 )
{
m_pInstance = new cStudentMgr;
}


return m_pInstance;
}


///< 생성된 static 인스턴스 삭제
void cStudentMgr::Destory(void)
{
if( m_pInstance != 0 )
{
delete m_pInstance;
m_pInstance = 0;
}
}






가장 위는 정적변수의 초기화이다.
생성자와 소멸자보다 위의 정적영역에서 초기화되고 있다.

(참고로 생성자에서 강조된 부분은 멤버 이니셜라이저라고 하는데, const 나 레퍼런스 변수처럼 선언과 동시에 초기화되어야 하는 변수들을 클래스에서 초기화해주는 방법이다. 여기서는 굳이 사용할 이유는 없지만 그냥 사용하고 있다-ㅅ-; 또한 상속시에 부모 생성자에 인자를 넘길때도 사용된다.)

그리고 아래 2개의 함수는 단 하나의 객체만을 생성 & 소멸시키는 기능을 하고 있다.
메인함수에서는 아래와 같이 사용된다.




#define ST_MGR cStudentMgr::GetInstance()   /// 사용하기 편리하도록


int main(void)
{
ST_MGR->SetID(1);


cout << ST_MGR->GetID() << endl;

ST_MGR->Destory();


return 0;
}




싱글톤패턴은 프로젝트에 단 하나뿐인 전역 클래스로서 주로 관리자 역할을 하는 클래스를 만들어 사용하는 방식이다.
static 이므로 객체 생성없이 정적 영역의 멤버변수와 함수를 어디서나 호출하여 사용할 수 있다.
특징은 전역변수와 마찬가지로 사용하기엔 편리하지만 남용할 경우 유지보수, 가독성, 재사용성 등에 문제가 발생한다.
따라서 프로젝트당 단 하나의 클래스만 전역으로 만드는 것이 좋으며, 프로젝트 팀장의 성향에 따라 싱글톤을 아예 못쓰게 하는 경우도 있다고 한다.




※ 참고: 정적(static) 함수란?
개념적으로는 정적 멤버변수만을 접근 가능한 함수이다.
클래스가 인스턴스되지 않아도 사용가능하다.
내부적으로는 다른 멤버함수와 달리 this 포인터를 넘기지 않는다.
때문에 c에서 callback으로 넘길때 유용하다(이건 아직 무슨 말인지 모르겠다).
this 포인터가 없으므로 상속해서 오버라이딩 불가능.
또한 c에서는 하나의 파일에서만 해당 함수를 사용하고 싶을때 static을 붙인다.

2012년 2월 20일 월요일

포풍같은 주말을 보내고..

뭐랄까.. 평생 다시 이런 일이 없을지도 모르는 주말이었는데

뭔가 완전히 불태우지 못한것 같아서 아쉽고 또 아쉬움만 남는다.

그래도 감동이었고...

뭔가 응어리같은게 풀린 느낌.. 죽어도 여한이 없다? 까지는 아니지만..ㅎㅎ

콘서트란건 한번은 좌석에서 전체를 보고 한번은 스탠딩을 뛰는것이 진리구나 라고 깨달음ㅋㅋ

아... 주말부터 오늘까지는 아무것도 손에 안잡혔다.

지금 해야할게 많은데...

그냥 에헤라디야 딩가딩가 하면서 살면 안될까요?