char* UTF8ToANSI(const char *pszCode)

{

BSTR    bstrWide;

char*   pszAnsi;

int     nLength;


// bstrWide 배열 생성 Lenth를 읽어 온다.

nLength = MultiByteToWideChar(CP_UTF8, 0, pszCode, lstrlen((LPCWSTR)pszCode) * 3 + 1, NULL, NULL);

// bstrWide 메모리 설정

bstrWide = SysAllocStringLen(NULL, nLength);


MultiByteToWideChar(CP_UTF8, 0, pszCode, lstrlen((LPCWSTR)pszCode) * 3 + 1, bstrWide, nLength);


// char 배열 생성전 Lenth를 읽어 온다.

nLength = WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, NULL, 0, NULL, NULL);

// pszAnsi 배열 생성

pszAnsi = new char[nLength];

// char 변환

WideCharToMultiByte(CP_ACP, 0, bstrWide, -1, pszAnsi, nLength, NULL, NULL);


// bstrWide 메모리 해제

SysFreeString(bstrWide);


return pszAnsi;

}

Posted by 모과이IT
,


Posted by 모과이IT
,

출처 : http://www.chrischoi.pe.kr/Lecture2/com_lec1.asp
최현진님의 홈페이지에서 퍼왔습니다.
=================================================================
COM+ 소개 

1997년 가을 마이크로소프트의 MTS 개발팀과 COM 개발팀이 통합된 후, 만들어낸 첫번째 제품이 COM+이며, COM+는 마이크로소프트의 COM(Component Object Model)과 마이크로소프트 트랜잭션 서버(Microsoft Transaction Server, MTS)의 다음 버전이다. 

COM+는 스레드 할당(thread allocation)과 보안(security) 같은 자원 관리 작업들을 처리함으로써, 개발자들의 수고를 덜었다. 그리고, 스레드 풀링(thread pooling), 객체 풀링(object pooling), JIT(just-in-time) 객체 활성화를 제공함으로써, 응용 프로그램의 확장성을 개발자의 수고 없이 향상시켰다. 또한, 트랜잭션 지원(transaction support)을 통해서 트랜잭션이 네트워크를 통해서 여러 데이터베이스에 분산되어 있더라도 데이터의 무결성도 보장한다. 

COM+를 사용하는 것은 여러 가지 면에서 이익이다. 시스템 관리자는 COM+ 응용 프로그램을 설치하고, 배포하고, 환경을 설정하기 위해서 사용할 수 있으며, 응용 프로그램 개발자는 작성한 컴포넌트를 응용 프로그램과 통합할 수 있으며, 개발 도구를 만드는 회사는 개발 도구가 COM+ 환경에서 작동하도록 개발하거나 변경할 수 있다. COM+는 Windows 2000 서버 운영체제 기반의 엔터프라이즈 웹 응용 프로그램, mission-critical 응용 프로그램, 분산 응용 프로그램 개발에 적용할 수 있으며, 비주얼 C++와 비주얼 베이직과 호환이 가장 잘 되도록 설계되었다. COM+는 Windows 2000 운영체제에 포함되어서 발표되었으며, 사용하려면 Windows 2000을 필요로 한다. COM+ 응용 프로그램은 Windows 98, Windows NT 클라이언트에서 사용할 수 있다. 

COM+는 컴포넌트 기반의 분산 응용 프로그램에 대한 단일 비전을 제공한다. 또한, 트랜잭션을 지원하는 다 계층 응용 프로그램을 개발하기 위한 도구들을 제공한다. COM+는 Microsoft Transaction Server 2.0 기술의 새로운 버전이며, Microsoft Component Object Model (COM)을 통합했고, 새로운 서비스들을 추가했다. 

COM+는 비동기적 COM을 지원함과 함께 스레드와 보안 분야에서 향상되었다. 새로운 서비스에는 새로운 분산 응용 프로그램 관리 및 패키징 서비스(컴포넌트 서비스) 뿐만 아니라, 동기화, 객체 풀링, queued component가 포함되었다. 

COM+는 새로운 스레딩 모델인 neutral apartment threading을 지원한다. NA는 컴포넌트가 순차적인 접근을 허용할 뿐만 아니라, 어느 스레드에서 든지 수행할 수 있다. 또한, role-base security, 비동기적 객체 실행, out-of-process 서버에서 실행 중인 객체 인스턴스를 참조하는 새로운 내장 모니커(moniker)를 추가했다. 

COM+의 기능들을 개략적으로 살펴보면 다음과 같다. 

역할 기반의 보안(Role-Based Security) 
COM+는 역할 기반의 보안과 프로세스 접근 권한 보안을 둘 다 지원한다. COM+의 보안 서비스는 프로그래밍 코드를 사용하지 않고 쉽게 보안을 구현할 수 있는 선언적인 보안 메커니즘을 제공하며, 프로그래밍으로 보안을 구현할 경우에도 보안 관리 코드를 쉽게 작성할 수 있는 방법들을 제공한다. 역할 기반의 보안 모델에서, 컴포넌트에 대한 접근 권한은 호출자가 속한 논리적 그룹 또는 역할에 의해서 부여되거나 거부된다. 또한, 보안의 수준은 메서드, 컴포넌트, 그리고 인터페이스 레벨까지 관리할 수 있다. 

스레딩(Threading) 
COM+는 새로운 스레딩 모델인 뉴트럴 스레딩(neutral threading)을 포함한다. 뉴트럴 스레딩 모델은 모든 스레드 타입에서 객체가 실행되도록 지원한다. 뉴트럴 아파트먼트 스레딩은 개발자가 뉴트럴 아파트먼트 내에서 실행하는 객체를 안전하게 만들지 않아도 자동으로 개체의 안전성을 보장하기 때문에 개발자가 직접 객체 스레드를 안전하게 구현하지 않아도 된다. 

개체 풀링(Object Pooling) 
개체 풀링은 응용 프로그램이 개체를 생성하고, 응용 프로그램의 필요에 따라 생성한 개체를 풀링하는 것을 허용한다. 비활성화된 개체의 풀(pool)에 있는 요소들을 재사용함으로써 시스템 자원을 절약할 수 있다. 모든 종류의 개체를 거의 다 풀링할 수 있으며, 개체 풀링은 구현하기도 쉽다. 

큐드 컴포넌트(Queued Component) 
큐드 컴포넌트는 COM+ 클라이언트가 COM+ 서버에 직접 연결되지 않은 상황에서도 서버에 있는 COM+ 개체를 호출할 수 있는 비동기적인 호출을 지원하는 메커니즘이다. COM+ 큐드 컴포넌트 서비스는 클라이언트와 서버가 연결되어 있다면 즉각적으로 실행되고, 클라이언트와 서버가 연결되어 있지 않다면 연결이 이루어질 때까지 실행을 연기할 수 있다. 큐드 컴포넌트 서비스는 실시간으로 처리되는 동기적 트랜잭션과 비동기적 트랜잭션을 함께 처리함으로써 엔터프라이즈 환경의 line-of-business 응용 프로그램을 위한 핵심 기능을 제공한다. 이 기술은 작업 현장에서 사용되는 클라이언트 응용 프로그램에 유용하게 사용될 수 있다. 예를 들어, 네트워크에 연결되지 않은 영업 사원이 고객을 상대로 하는 업무를 수행한 후에, 사무실로 돌아와서 네트워크에 연결하면, 트랜잭션이 실행될 수 있도록 응용 프로그램을 만들 수 있다. 이런 비동기적 메시지 큐잉은 마이크로소프트 Windows 2000의 메시징 서비스(Messaging Services)를 사용한 것이다. 

COM+ 이벤트(COM+ Events)
COM+ 이벤트는 이벤트를 발생시키는 Publisher와 이벤트를 수신하는 Subscriber를 연결시켜주는 컴포넌트 기반의 이벤트 시스템이다. Publisher는 subscriber에게 이벤트를 보내며, subscriber는 publisher로부터 subscription을 받는다. 이벤트 객체를 호출 할 수 있는 모든 COM+ 개체는 Publisher가 될 수 있으며, 이벤트 인터페이스의 메서드를 제공하는 단순한 COM+는 subscriber가 될 수 있다. 

자동 트랜잭션(Automatic Transactions)
트랜잭션은 분산 시스템에서 데이터의 무결성을 보장하는 메커니즘이다. COM+의 트랜잭션 서비스는 트랜잭션을 생성하고, 사용하고, 트랜잭션 완료의 허용 여부를 결정하는 COM 객체를 쉽게 만들 수 있는 방법을 제공한다. COM+는 마이크로소프트 트랜잭션 서버(MTS)의 모든 기능들을 수용했으며, “Auto-done”을 추가했다. “Auto-done”은 예외상황이 발생했을 경우에는 시스템이 자동적으로 SetAbort를 수행해서 모든 작업들을 롤백하며, 예외상황이 발생하지 않고 정상적으로 작업이 완료되었다면 SetComplete를 호출해서 작업을 커밋하는 것이다. COM+는 컨텍스트(context)라는 특별한 환경으로 컴포넌트를 지원한다. 컨텍스트는 컴포넌트의 실행 환경을 정의하는 확장된 속성들의 집합을 제공한다. 

응용 프로그램과 컴포넌트 관리(Application and Component Administration) 
COM+에는 컴포넌트에 대한 메타 데이터를 저장하는 새로운 등록 데이터베이스인 RegDB가 추가되었다. 이 데이터베이스는 컴포넌트의 활성화를 위해서 COM+가 필요로 하는 정보를 관리하며, 시스템 레지스터리 대신에 사용되도록 최적화되어져 있다. 추가로, COM+는 COM+ Catalog라는 트랜잭션을 지원하고 스크립트로 접근 가능한 인터페이스를 제공하며, 이것으로 RegDB 내의 정보에 접근할 수 있다. 컴포넌트 서비스 관리 도구는 컴포넌트 관리자에게 스크립트로 접근 가능한 인터페이스를 제공하며, 클라이언트 사이드와 서버 사이드의 다 계층 응용 프로그램의 배포를 용이하게 한다. 

동기화 서비스(Synchronization Services) 
동기화 서비스는 컴포넌트에 여러 개의 스레드들이 동시에 접근할 때 생기는 위험을 방지하는 기반 구조이며, 코드를 작성하지 않고도 쉽게 컴포넌트의 동기화 요구 설정을 지정할 수 있는 수단을 제공한다. 또한 프로그램으로 구현할 경우에도 동기화 관련 코드를 쉽게 작성할 수 있는 방법들을 제공한다. 



COM에서 COM+로 

COM은 마이크로소프트의 이진 규격으로서, 객체가 어떻게 상호작용 할 것인지를 정의한다. COM은 Windows 3.1 이후의 모든 마이크로소프트의 Windows 운영체제와 16비트, 32비트에서 사용이 가능하다. 

COM은 프로그래밍 언어가 아니며, 특정한 언어의 기능이 아니다. 그래서 함수 포인터를 지원하는 C, C++, 파스칼 등의 언어와 함수 포인터를 직접적으로 지원하지 않는 비주얼 베이직, 비주얼 J++와 같은 언어들은 COM 개체를 호출하거나 생성할 수 있도록 확장된 기능을 제공한다. 

개발자들은 단일 사용자 어플리케이션을 작성할 때, COM을 사용해왔으며, 분산된 환경에서 다중 사용자를 지원하기 위해서 DCOM을 사용했다. 그리고 다중 사용자를 지원하는 DCOM을 구현하는 여러 가지 문제들을 해결하기 위해서 마이크로소프트 트랜잭션 서버(MTS)를 사용했다. 

DCOM은 COM이 네트워크에 걸쳐 분산되어 있는 것을 말한다. DCOM 개체의 클라이언트는 마치 개체가 자신의 주소 영역에 있는 것처럼 개체를 이해한다. 하지만, DCOM은 다중 사용자를 고려해서 개발되어야 하기 때문에 고도의 기술과 많은 시간, 그리고 노력을 필요로 했다. 

DCOM을 개발하는 개발자의 대부분의 노력은 Business Logic을 구현하기 보다는 분산된 환경의 다중 사용자에 의해서 호출되는 DCOM의 상태를 관리하는 코드를 구현하는데 소비되었으며, 이 부분이 개발자들에게는 해결해야 할 골치거리 였으며, 이것은 MTS라는 미들웨어를 통해서 해결할 수 있었다. 

COM+는 COM과 DCOM이 가지고 있는 대부분의 개념을 포함하는 개념이다. 쉽게 말하자면, COM+는 COM을 DCOM으로 동작하도록 구현해주는 인프라이다. 

Windows NT 환경에서 동작하던 MTS는 COM위에 놓인 개별적인 Wrapper로서 자신의 서비스를 제공했다. 클라이언트가 MTS에 등록된 컴포넌트를 호출하면 MTS는 컨텍스트 개체(컨텍스트 Wrapper)를 생성하고 그것을 서버 측의 스터브(Stub)와 실제 컴포넌트 사이에 끼워 넣는다. 그리고 난 후 개체에 대한 모든 호출은 컨텍스트 Wrapper로 전달되며, MTS는 컨텍스트 Wrapper를 통해서 보안 점검이나 개체 풀링, 트랜잭션 관리 같은 작업들을 수행한다. 

Windows 2000의 COM+는 MTS의 다음 버전으로, MTS의 방식과 기존의 COM 방식을 결합한 것이다. COM+는 더 이상 Wrapper 계층을 사용하지 않으며, Policy 개체를 통해서 작업을 수행한다. 클라이언트에서 개체의 메서드를 호출하면 클라이언트와 서버 측에 모두 Policy 개체가 생성된다. 클라이언트의 호출은 RPC 채널을 통해서 서버 개체를 호출한다. 이런 관계 사이에 클라이언트 Policy 개체와 서버 Policy 개체가 RPC 채널 양쪽에 생성되며, 클라이언트와 서버의 상호작용을 관장한다


Posted by 모과이IT
,

http://adolys.tistory.com/entry/%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EB%A9%94%EC%86%8C%EB%93%9CInstance-Method%EC%99%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A9%94%EC%86%8C%EB%93%9CClass-Method

'개발지식창고 > C++' 카테고리의 다른 글

DLL 제작 기법  (0) 2017.06.30
COM+ 서버  (0) 2015.02.09
#pragma pack(push, 1)  (0) 2011.11.28
형변환 (Typecasting) - const_cast, reinterpret_cast, static_cast, dynamic_cast  (0) 2010.11.14
예외(Exception) 처리 try, catch, throw  (0) 2010.11.14
Posted by 모과이IT
,

기본적인 윈도우 환경에서의 데이터 정렬(Data alignment)은 4byte를 기준으로 이루어지게 된다.

struct A

{

    char a;

    short b;

};

위와 같은 구조체가 있다. 윈도우에서는 어떻게 메모리를 잡을까 생각을 해보자.

윈도우는 4byte가 가장 작은 메모리 할당량이다. 그러므로 어떠한 변수 하나를 잡기 위해서 4byte를 기준으로 계산하게 된다.

short = 2byte, char = 1byte이므로 4byte를 할당 받아 short을 먼저 2byte 할당하고 남는 2byte에 char를 할당하게 된다.

즉, 3byte를 사용하기 위해서 4byte의 메모리를 사용하게 되는 것이다.

 

하지만, 한가지 고려해야하는 부분은 컴파일러에 있다. Visual Studio는 기본적으로 8byte를 사용한다.

위 구조체를 할당하기 위해서는 8byte가 필요하게 되는 것이다.

 

#pragma pack(push, 1)

구조체

#pragma pack(pop)

 

위와 같은 방식으로 선언을 해준다면, Data alignment가 변하게 되는 것이다.

위의 선언은 데이터 정렬 기준은 1byte로 한다는 것이다.

즉, struct A는 위의 환경에서는 3byte만이 필요하게 되는 것이다.

메모리가 필요할 경우 무조건 1byte씩을 할당을 하게 되므로 불필요한 공간이 없이 필요한 공간만을 할당하게 되는 것이다.

 

#pragma pakc(pop)을 하지 않을 경우는 #pragma pack(push, 1)로 선언된 이후의 부분은 전부 적용이 되는 것이다.

이것에 신경을 잘 쓰지 않으면 프로젝트가 엉망이 될 수 있다. 주의!!!!

예) 프로젝트 중간에 pack을 했다면 같은 값을 pack이 된 곳과 안된곳의 값이 다르게 표현이 되게 된다.

      이유는 구조체 자체의 메모리 할당이 서로 다르기 때문에 생기게 되는 곳이다.

 

가장 유용하게 사용되는 부분은 네트워크 통신에 사용되는  packet을 정의할 때 가장 유용하게 사용된다.

network packet은 서버 클라이언트에서 같이 사용하기 때문에 문제가 되는 점은 발견되지 않으며, network packet의 사이즈를 적절하게 해주기 때문에 더욱 유용한 것이다.

 

추가)

#pragma warning(disable:4996)

...

#pragma warning(default:4996)

이는 컴파일 단계에 발생되는 단순한 warning을 무시하는 기능을 한다.

crt function을 사용하거나 하는 등의 단순한 warning에서만 사용을 해야함...

처음 disable에서는 무시를 하게 되고, default에서는 다시 원상태로 변경을 하게 된다.


Posted by 모과이IT
,
형변환 (Typecasting)  
 : 그동안 명시적인 형변환을 할때는 괄호를 사용했었는데 C++에서는 이를 대체 할 수 있는 4가지 종류의 형변환 연산자가 추가 되었다. 그럼 그동안에 사용했던 명시적 형변환을 계속 쓰지 않고 새로운 형변환 종류가 추가 되었을까? 일단 기존의 C 스타일 형변환은 두가지 문제점이 있다. 첫번째는 C 스타일의 형변환(컴파일 타입 형변환)은 눈에 잘 띄지도 않고 찾아내기 힘이 든다는 점이다. 사용자가 프로그램을 짜다 보면, 형변환 말고도 괄호를 사용하는 부분이 많기 때문이다. 뭐 눈에 잘 안띈다고 단점이될까? 
  명시적 형변환을 수행한다는 것은 암시적인 형변환이 불가능하다는 뜻이고, 암시적인 형변환이 불가능하다는 것은 컴퓨터가 생각하기에는 문제의 소지가 있다는 뜻이다. 다음으로 C 스타일의 형변환은 형변환의 의도를 구별해내기가 힘들다는 문제점도 있다. C++에서의 형변환 연산자는 그 용도에 따라서, 안전한 형변환, const 속성을 제거하는 형변환, 위험한 형변환, 클래스 타입간의 형변환 등으로 나뉘어져 있다. 그렇기 때문에 C++ 연산자를 사용해서 형변환을 하면 코드를 읽는 사람이 형변환의 의도를 쉽게 알아챌 수 있다. 컴퓨터 역시 코드를 작성한 사람의  의도를 파악 할 수 있기 때문에 컴퓨터가 개발자의 실수를 발견해서 경고해주는 것도 가능하다. 

 const_cast  
 : const_cast는 어떤 타입에서 const 속성이나 volatile 속성을 제거 할때 사용한다. 
* volatile속성 
 : 변수를 정의할때 volatile 키워드를 붙여줄 수 있는데 컴퓨터는 가끔씩 어떤 이유에서 변수를 상수로 만들어버리는 작업을 하게 되는데,  volatile로 지정한 변수는 그 작업에서 열외가 된다. 대부분의 경우 변수를 상수로 만드는 작업은 프로그램의 성능을 높이는데 도움이 되지만, 어떤 상황에서는 문제를 일으키기 때문이다. (지금은 쓸데가 없지만, volatile은 Multi threading과 관련된 곳에서 사용할 일이 생길 수 있다.) 다음은 const int 타입을 int 타입으로 형변환 하는 코드이다. 
  1. const int value = 100;   
  2. int i = const_cast<int> (value);  

 reinterpret_cast  
 : reinterpret_cast는 일반적으로 허용하지 않는 위험한 형변환을(무조건적인 형변환 ) 할때 사용한다. 즉, 그 안의 데이터가 어떤 객체이던 그저 비트열로만 보고 원하는 형으로 강제로 변환을 한다는 것이다. 예를 들어 포인터를 정수로 변환하는 작업 등이 이에 해당 되겠다.
  1. int a, b;   
  2. a = reinterpret_cast<int>(&b);  

 static_cast  
 : static_cast는 가장 일반적인 형태의 형변환을 할때 사용한다. 만약에 A타입에서 B타입으로의 암시적인 형변환이 가능하다면 static_cast를 사용해서 B타입에서 A타입으로 형변환 할 수 있다. 예를 들어 double 타입을 char 형으로 형변환하는데 사용할 수 있겠다.
  1. double d = 10.0;   
  2. char c;   
  3. c = static_cast<char>(d);  
 static_cast는 명시적인 형변환이기는 하지만 대체적으로 안전한 형변환이라고 볼 수 있다. 아마도 우리가 수행하는 대부분의 형변환은 여기에 속할 것이다. 

 dynamic_cast  
 : 유일하게 C 스타일의 형변환으로는 흉내낼 수 없는 것이 dynamic_cast이다. dynamic_cast는 서로 상속 관계에 있는 클래스간에 형변환을 할 때 사용한다. 더불어 형변환을 수행하는 동시에 이 형변환이 안전한 것인지까지 검사 해준다. 그래서 dynamic_cast는 실시간에 형검사를 하거나 형변환할 때 사용한다.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Parent   
  5. {   
  6. public:   
  7.     virtual void Print( ) {}   
  8. };   
  9. class Child1 : public Parent   
  10. {   
  11. public:   
  12.     void Print( ) { cout << "class Child1 no problem" << endl; }   
  13. };   
  14. class Child2 : public Parent   
  15. {   
  16. public:   
  17.     void Print( ) { cout << "class Child2 no problem" << endl; }   
  18. };   
  19. void main( )   
  20. {   
  21.     Parent *p = new Child1;   
  22.     Parent *p1 = new Parent;   
  23.   
  24.     Child2* faile_child = dynamic_cast<Child2*> (p1);   
  25.     if( NULL == faile_child )   
  26.         cout << "Child2 Null 반환." <<endl;   
  27.     else  
  28.         faile_child->Print();   
  29.   
  30.     Child1 *pChild1 = dynamic_cast<Child1*>(p);    
  31.     if( NULL == pChild1 )   
  32.         cout << "Child1 Null 반환." <<endl;   
  33.     else  
  34.         pChild1->Print();   
  35.   
  36.     Child2 *pChild2 = dynamic_cast<Child2*>(p);   
  37.     if( NULL == pChild2 )   
  38.         cout << "Child2 Null 반환." <<endl;   
  39.     else  
  40.         pChild2->Print();   
  41.   
  42.     try  
  43.     {   
  44.         Child2& C2 = dynamic_cast<Child2&> (*p);   
  45.     }   
  46.     catch (bad_cast& e)   
  47.     {   
  48.         cout<<"bad_cast currupt"<<endl;   
  49.     }   
  50. }  

 위 예제에서 본것처럼, dynamic_cast는 다운 캐스트, 즉 부모 클래스 타입에서 자식 클래스 타입으로 형변환 할때 유용하게 사용할 수 있다. 다운 캐스트는 포인터나 레퍼런스가 가리키고 있는 객체의 실제 타입이 무엇이냐에 따라서 안전할 수 있고 위험할 수도 있는데 dynamic_cast가 알아서 안전여부는 검사를 해준다. 
 만약에 형변환에 문제가 있는 경우라면 dynamic_cast 연산자는 NULL 값을 반환하거나 bad_cast 예외를 던지게 된다.(위 결과 처럼 말이다.) 포인터의 형변환이라면 NULL을 반환함으로써 문제 상황을 알릴 수 있지만, 레퍼런스의 형변환인 경우에는 어떤 특정한 값은 반환하는 것이 불가능하므로 bad_cast 예외를 던지게 된다. (bas_cast 에외 역시 C++의 다른 예외 클래스들처럼 exception 클래스를 상속받았다.)
 위에서는 의미도 없는 가상함수를 사용했는데, 가상함수가 하나도 없는 클래스는 dyynamic_cast를 사용할 수 없기 때문이다. 이는 RTTI의 내부 구현과 관련이 있다. 위의 예제는 나머지 자식 클래스들도 상속받아 물려받은 가상 함수가 있는 셈이므로 dynamic_cast를 사용 할 수 있는 것이다. 만약 클래스가 가상함수를 하나도 가지지 않는다면 해당 클래스는 타입으로는 RTTI 를 이용할 수 있지만 객체로는 RTTI 를 이용할 수 없다.

 그럼 검사도 해주고 형변환도 해주는 dynamic_cast가 만능이냐? 아니다. static_cast<Child1*>(p); 이와 같이 static_cast를 해줄 수도있다. 하지만 이는 아무런 검사도 하지 않고 형변환을 하기 때문에, 실제론 잘못동작할수 있는 코드가 아무런 경고나 에러없이 컴파일되게 되는 불안요소를 가지고 있다고 할 수 있죠. 
 dynamic_cast는 RTTI를 이용해서 런타임시에 형을 체크하기 때문에, 잦은 dynamic_cast는 눈에 띌 정도의 퍼포먼스 저하의 원인이 될수 있습니다. 그렇기 때문에 자신이 하는 형변환하는 것이 안전하고 확실하다고 생각할때만 static_cast를 이용하고, 확신할수 없을 경우는 dynamic_cast를 이용하여 널포인터를 체크하는 것이 바람직하다.

 RTTI (Runtime Type Information)  
 : RTTI (Runtime Type Information)실행시간에 객체의 타입에 대한 정보를 얻을 수 있는 기능을 말한다. C++은 클래스의 객체만 가지고선 어떤 클래스의 객체인지 알수 있는 방법이 원래 없기 때문이다. 형변환 중 dynamic_cast를 할려면 RTTI가 필요 한데, 우리가 많이 쓰는 Visual Studio는 기본적으로 RTTI 기능을 사용하지 않게 설정되어 있다. 왜냐하면 RTTI의 특성상 객체를 생성할때마다 그 객체 내부에 타입 정보와 상속 정보를 넣어두기 때문에 속도(퍼포먼스)의 저하가 일어나기 때문이다. 
그래서 RTTI 기능 및 dynamic_cast를 사용하기 위해서는 비주얼 스튜디오의 프로젝트 설정을 변경 시켜 줘야 한다. 프로젝트의 Properties에 들어가 C++ -> Language -> Enable Run-Tie Type Information을 Yes(/GR)로 바꿔주면 된다.

  그럼 이 RTTI와 dynamic_cast는 어떻게 작동을 하는 것일까? 컴파일러는 RTTI 와 객체를 연결하기 위해서 가상함수 포인터 테이블을 이용을 한다. 원래 C++ 언어의 가상함수 포인터 테이블은 순수한 가상함수에 대한 함수 포인터 배열이다. RTTI 와 객체의 연결을 위해 C++ 언어는 가상함수 포인터 테이블 앞에 4 byte 를 만들고 이것을 RTTI 와의 연결 고리로 사용한다. 
 프로그램이 dynamic_cast 를 이용하여 캐스트를 한 경우 실행 코드는 dynamic_cast 의 표현식에 기술된 객체를 이용하여 RTTI 포인터 테이블을 검색하고, 만약 RTTI 포인터 테이블 상에 일치하는 RTTI 가 존재 한다면 표현식에 기술된 객체의 타입을 변환하여 반환하고, RTTI 포인터 테이블 상에 일치하는 RTTI 가 존재 하지 않는다면 dynamic_cast 는 NULL(0) 을 반환을 할 것이다. 
Posted by 모과이IT
,
예외 처리  
 : 예외란 무엇인가? 예외란 일반적이지 않는 상황을 말하는 것이다. 에러가 아니라는 말이다. 이런 일반적이지 않은 프로그램의 흐름을 처리 하는것을 예외 처리 라고 한다. 

 Try, Catch, Throw  
 : 일단 예외를 처리 하려면, 그에 맞는 문법을 써야 할 것이다. 
  1. try  
  2. {    
  3.     //예외 발생 예상지역   
  4. }   
  5.   
  6. catch(처리되어야할 예외의 종류)   
  7. {   
  8.     //예외를 처리하는 코드가 존재할 위치   
  9. }  
 위는 try, catch 문이다. try에는 예외라 불릴 상황을 가진 문장을 집어 넣는 것이다. (try 문은 무조건 실행이 된다.) 처리되어야 할 예외의 종류를 받아 catch 문에서는 이 예외에 대한 처리를 해주는 것이다. (명시적으로 cout문을 이용해 예외가 발생했다고 처리해 주거나 한다)
  1. if ( 조건 )   
  2.     throw ex;  
 다음은 throw로 위와 같이, "조건에 합당하면 예외를 던진다" 예제 소스코드를 보면 이해가 더 쉬울 것이다.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. int main(void)   
  5. {   
  6.     int num;   
  7.   
  8.     cout<<"input number: ";   
  9.     cin>>num;   
  10.   
  11.     try{   
  12.         if(num>0)   
  13.             throw 10;  // int형 예외 전달.   
  14.         else  
  15.             throw 'm';  // char형 예외 전달.    
  16.     }   
  17.     catch(int exp){   
  18.         cout<<"int형 예외 발생"<<endl;   
  19.     }   
  20.     catch(char exp){   
  21.         cout<<"char형 예외 발생"<<endl;   
  22.     }   
  23.     return 0;   
  24. }  

 입력을 받는다고 가정하자. 이 입력받는 곳에는 숫자만 들어와야 하는데, 만약 문자가 들어 온다면 이 상황에서 문자가 들어온 것이 바로 예외 상황이다. 이런 예외 처리를 만약 문자형을 받으면 "예외를 던져(throw exception)" 그 예외에 해당하는 상황을 출력하는 간단한 예제이다. 여기에서 예외 전달을 위해 int 타입도 예외를 전달하는 모습을 보여주고 있다.
Posted by 모과이IT
,
템플릿의 동작원리는 함수 오버로딩(Function Overloading)과 유사한 형태로 구성이 된다. 다음 템플릿화된 소스코드를 보자.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. template <typename T>   
  5. T Add(T a, T b)   
  6. {   
  7.     return a+b;   
  8. }   
  9.   
  10. void main()   
  11. {   
  12.     cout << Add(10,20);   
  13. }  
 실제 위의 소스 코드는 어떻게 동작을 할까? 실제 main 함수에서 Add(10,20); 이 문장에서는 실제 아래와 같은 함수가 만들어진다.
  1. int Add(int a, int b)   
  2. {   
  3.     return a+b;   
  4. }  
 그래서 우리가 컴파일 하고 실행할 때 실제로 호출 하는 함수는 템플릿 기반으로 만들어진 int Add 함수를 컴파일러가 자동적으로 생성하고 호출 하는 것이다. 우리가 지금까지 컴파일러가 코드를 자동적으로 생성해 주는 경우는 없었지만, 컴파일 타임에 이런 인스턴스화가 일어난다. 이렇게 함수 템플릿이 인스턴스화 되어 나온것이 바로 템플릿 함수이다. 함수 템플릿템플릿 함수 이런 차이점이 있다.
 클래스 템플릿도 마찬가지이다. 하지만 클래스 템플릿을 만들 때, 주의할 점이 하나있다. 일반적으로 우리는 클래스를 만들때, (지금까지 예제는 main 함수가 있는 cpp 한곳에 만들었지만..) 헤더(header) 파일과, cpp 파일로 분리 해서, 헤더에는 선언만 cpp에는 정의만 이렇게 나눠서 작성을 할 것이다. 
 앞서 말했듯이, 함수 템플릿이 인스턴스화 되어 템플릿 함수가 실제로 호출이 일어나 프로그램을 실행하는 것이다. 템플릿 함수는 컴파일러가 만드는데 이렇게 파일을 나눠서 구현을 해버리고, main 함수에서 실제 호출 하려고 하면, 에러가 나는 것을 알 수 있다. 
 cpp에서 정의 부분을 넣는데, cpp의 정의 부분은 링커(Linker)가 호출해주므로 이런 문제가 발생하는것이다. 그래서 만약 우리가 클래스 템플릿을 만들어서 쓰고 싶다면 헤더에 선언과 정의를 한번에 다 하는 것이 좋다.
Posted by 모과이IT
,
함수 템플릿  
 템플릿의 종류에는 함수 템플릿, 클래스 템플릿 이 두가지로 나뉠 수 있다. 책마다 함수 템플릿 또는  템플릿 함수로 이 두가지로 표기가 되어 있는데, 이런 명사 두개가 오는 단어들은 뒤에가 진짜다. 우선 함수 템플릿, 템플릿 함수의 의미를 정리 해보자.
 - 함수 템플릿 : 함수를 기반으로 구현이 된 템플릿 (함수가 아니라는 뜻이다)
 - 템플릿 함수 : 템플릿을 기반으로 한 함수라는 뜻
 우리가 앞서 본 템플릿 예제는 함수 템플릿이다. 이 함수 템플릿에 대해 좀 더 자세히 알아 보자.

 둘이상의 타입에 대한 템플릿  
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. template <typename T> // 함수 템플릿 정의   
  5. void ShowData(T a, T b)    
  6. {   
  7.     cout<<a<<endl;   
  8.     cout<<b<<endl;     
  9. }   
  10.   
  11. int main(void)   
  12. {   
  13.     ShowData(1, 2);   
  14.     ShowData(3, 2.5); //error   
  15.   
  16.     return 0;   
  17. }  
 위와 같이 showdata의 인자값의 데이터 자료형을 각각 달리 해주면, 에러가 나오는 것을 알 수 있다. 이 경우 아래와 같이 사용하면 된다.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. template <typename T1,typename T2> // 함수 템플릿 정의   
  5. void ShowData(T1 a, T2 b)    
  6. {   
  7.     cout<<a<< " ";   
  8.     cout<<b<<endl;     
  9. }   
  10.   
  11. int main(void)   
  12. {   
  13.     ShowData(1, 2);   
  14.     ShowData(3, 2.5);   
  15.   
  16.     return 0;   
  17. }  


 템플릿 특수화  
 : 특수화란 범위를 좁히는 것을 말한다. 다음과 같은 경우가 있다고 한번 가정해 보자. 
  1. #include <iostream>   
  2. using std::endl;   
  3. using std::cout;   
  4.   
  5. template <typename T> // 함수 템플릿 정의   
  6. int SizeOf(T a)    
  7. {   
  8.     return sizeof(a);   
  9. }   
  10.   
  11. int main(void)   
  12. {   
  13.     int i=10;   
  14.     double e=7.7;   
  15.     char* str="Good morning!";   
  16.   
  17.     cout<<SizeOf(i)<<endl;   
  18.     cout<<SizeOf(e)<<endl;   
  19.     cout<<SizeOf(str)<<endl;   
  20.   
  21.     return 0;   
  22. }  

 원래 있던 sizeof 함수를 템플릿화를 시켰다. 뭐 이상없이 찍힌다. 캐릭터형 포인터는 4가 찍히는 것이 맞겠지만, 사용자는 그 데이터형의 사이즈는 관심없고, 문자열이 들어 있다면 그 문자열의 크기가 얼마 인지 알고 싶다고 해보자. 하지만 위와 같이 템플릿화한 경우에는 변수 사이즈만 찍힐 것이다. 그래서 캐릭터형 포인터의 데이터 타입을 받으면 그 경우에는 문자열 크기를 알아 보는 strlen 함수를 써서 문자열 길이를 반환하는 것. 즉, 어떠한 경우에서만 특별히 다른 행동을 했으면 좋겠다. 이것이 바로 특수화다. 문법 사용은 다음과 같이 쓴다.
  1. #include <iostream>   
  2. using std::endl;   
  3. using std::cout;   
  4.   
  5. template <typename T> // 함수 템플릿 정의   
  6. int SizeOf(T a)    
  7. {   
  8.     return sizeof(a);   
  9. }   
  10.   
  11. template<> // 특수화   
  12. int SizeOf(char* a)    
  13. {   
  14.     return strlen(a);   
  15. }   
  16.   
  17. int main(void)   
  18. {   
  19.     int i=10;   
  20.     double e=7.7;   
  21.     char* str="Good morning!";   
  22.   
  23.     cout<<SizeOf(i)<<endl;   
  24.     cout<<SizeOf(e)<<endl;   
  25.     cout<<SizeOf(str)<<endl;   
  26.   
  27.     return 0;   
  28. }  

 클래스 템플릿  
 : 클래스를 템플릿 하고자 하면, 원하는 자료형만 T로 바꾸면 된다. 아래의 클래스를 예로 들어 보자. 
  1. class Data   
  2. {   
  3.     int data;   
  4. public:   
  5.     Data(int d){   
  6.         data=d;   
  7.     }   
  8.     void SetData(int d){   
  9.         data=d;   
  10.     }   
  11.     int GetData(){   
  12.         return data;   
  13.     }   
  14. };  
 템플릿화~
  1.   
  2. template <typename T>   
  3. class Data   
  4. {   
  5.     T data;   
  6. public:   
  7.     Data(T d){   
  8.         data=d;   
  9.     }   
  10.     void SetData(T d){   
  11.         data=d;   
  12.     }   
  13.     T GetData(){   
  14.         return data;   
  15.     }   
  16. };  
 그럼 이렇게 선언한 클래스 템플릿을 main에서는 어떻게 사용할까? 이전처럼 사용하면 되는 것일까? Data d1(10); 이렇게 사용하면 문제가 된다. 무엇이 문제가 되는지 한번 자세히 알아 보자.
 우선 객체 생성 순서에 대해 생각해 보자. 메모리 할당 -> 생성자 호출.... 이런식이다. 그러면 Data d1(10); 이 문장에서  d1 이라는 이름으로 메모리 공간을 할당을 해야 할 것이다. 하지만 우리는 클래스 템플릿을 사용하고 있기 때문에,T가 어떤 데이터형을 사용해야 할건지가 결정이 나야 메모리 할당이 이루어는 구조를 가지고 있다. T가 결정나는 시점은 생성자가 호출되어야만 (괄호 안의 10이 호출 되어야만..) T가 int형 데이터 인지 알 수 있으므로 이전과 같은 문법을 사용한다면 메모리 할당을 전혀 하지 못하게 되는 것이다. 
 그래서 템플릿 클래스에서 Data 객체를 만들기 위해서는 생성자를 통해서 전달되는 인자의 정보를 참조하는 시기가 늦기 때문에 구체적으로 어떤 타입으로 템플릿을 구체화 시킬지 명시적으로 선언을 해줘야 한다. 아래의 실사용 예제를 보자.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. template <typename T> //Data 라는 템플릿 Data<T>가 이것의 이름이 된다.   
  5. class Data   
  6. {   
  7.     T data;   
  8. public:   
  9.     Data(T d);   
  10.     void SetData(T d);   
  11.     T GetData();   
  12. };   
  13.   
  14. template <typename T>   
  15. Data<T>::Data(T d){   
  16.     data=d;   
  17. }   
  18.   
  19. template <typename T>   
  20. void Data<T>::SetData(T d){   
  21.     data=d;   
  22. }   
  23.   
  24. template <typename T>   
  25. T Data<T>::GetData(){   
  26.     return data;   
  27. }   
  28.   
  29.   
  30. int main(void)   
  31. {   
  32.     Data<int> d1(0);   
  33.     d1.SetData(10);   
  34.   
  35.     Data<char> d2('a');   
  36.   
  37.     cout<<d1.GetData()<<endl;   
  38.     cout<<d2.GetData()<<endl;   
  39.   
  40.     return 0;   
  41. }  

Posted by 모과이IT
,
템플릿?  
 : Template 이라는 단어는 모형자라는 의미를 가진단어로, C++에서 템플릿은 어떤 제품을 만들어내는 틀, 예를 들어 붕어빵에 비교해 보자면, 붕어빵을 만들어 내는 틀을 템플릿이라 말 할 수 있다. 템플릿의 특징은 기능은 이미 결정되어 있지만, 데이터 타입은 결정되어 있지 않는다는 특징을 가지고 있다. 아래에는 Sub라는 함수가 있다.
  1. int Sub(int a, int b)   
  2. {   
  3.     return a-b;   
  4. }  
 Sub라는 함수는 두개의 int 형 데이터를 서로 빼주는 그런 함수이다. 이런 함수를 한번 템플릿화 해보겠는데, 템플릿의 특징은 무엇이라 했는가? 바로 기능은 결정되어 있고 데이터 타입은 결정되지 않는 그런것이라고 언급을 했었다. 여기서 기능은 Sub(바로 빼주는) 거라고 할 수 있고, 데이터 타입은 int 이다. 이것은 유념해 두고 템플릿화 해보면 다음과 같이 템플릿화 할 수 잇다. 
  1. template <typename T>   
  2. T Sub(T a, T b)    
  3. {   
  4.     return a-b;   
  5. }  
 여기서 template <typename T>는 T라는 타입이름에 대해서 그 아래 존재하는 함수를 템플릿화 하겠다는 의미이다. 여기서 T라는 자료형은 Sub라는 이 함수를 사용할 때, 결정된다. 그럼 이 템플릿화 한 함수를 한번 직접 써보자.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. template <typename T1, typename T2> // 함수 템플릿 정의   
  5. void ShowData(T1 a, T2 b)    
  6. {   
  7.     cout<<a << " ";   
  8.     cout<<b<<endl;     
  9. }   
  10.   
  11. int main(void)   
  12. {   
  13.     ShowData(1, 2);   
  14.     ShowData(3, 2.5);   
  15.   
  16.     return 0;   
  17. }  

이와 같이 템플릿(Template)은 자료형에 독립적으로 쓸 수 있다는 장점을 가지고 있다. 
Posted by 모과이IT
,