블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2731)
Unity3D (814)
Programming (474)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (57)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (51)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
03-29 07:22
 형변환 (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) 을 반환을 할 것이다. 

출처 : 
반응형

'Programming > C/C++' 카테고리의 다른 글

enum, 보다 나은 enum  (0) 2011.01.13
IME 소스 (한글)  (2) 2010.11.26
GameLoop (번역)  (0) 2010.09.16
IME 관련  (0) 2010.09.10
TLS (Thread Local Strorage)  (0) 2010.08.30
Posted by blueasa
, |