본문 바로가기

Windows Developer/C++

[C++] 형변환 연산자

[출처] http://blog.daum.net/coolprogramming/62

C++언어는 C언어를 포함하므로 C언어의 형변환을 사용할 수 있지만 C++언어 자체의 형변환 연산자를 제공합니다.

(EC++에서 C스타일의 형변환보다는 C++ 스타일의 형변환을 사용하라고 권장합니다. C++ 스타일의 형변환을 사용하면 잘못된 형변환의 위험에서 좀 더 안전하며 설계자의 의도를 좀 더 확실히 할 수 있습니다.)

 

  • ( ) C언어의 형변환 : 만능 형변환 = 무식한 형변환 = 불가능은 없다.
    - 컴파일 타임 형변환 -

  • static_cast : 상식적인?! 형변환입니다. 여기서 상식적인 형변환이라 말한 이유는 상식적으로 생각했을 때 가능할 것 같은 형변환이기 때문입니다.(암시적 변환이 가능한 형변환을 명시적으로 변환한다든지, 수치적인 자료형을 변환한다든지, void*을 원형으로 변환할 때 사용합니다.)   - 컴파일 타임 형변환 -

  • const_cast : C++ 스타일 형 변환에서 유일하게 const를 깨는 형 변환 연산자입니다.
     - 컴파일 타임 형변환 -

  • reinterpret_cast : C++ 스타일의 형변환에서 가장 무식한 형변환입니다. const만 깰 수 없고 C언어의 형변환처럼 모두 가능합니다. 구현 환경(implementation)에 의존적이며 사용할 일이 거의 없습니다. - 컴파일 타임 형변환 -

  • dynamic_cast : 실행시간에 부모 형식을 자식 형식으로 다운캐스팅(downcasting) 할 때 사용합니다. - 런 타임 형변환 -

 

1, static_cast 연산자

 static_cast는 컴파일 시간에 일반적으로 이루어지는 형변환에 사용됩니다.

 

명식적인 형변환에 사용합니다.

 #include <iostream>
using namespace std;

void main( )
{
    int n = 10;
    double d = 3.5;

    cout << n << ", " << d << endl;
    n = d;      //암시적 형변환
    cout << n << ", " << d << endl;
    n = (double)d; // C 스타일의 명시적 형변환
    cout << n << ", " << d << endl;
    n = static_cast<double>(d); //C++ 스타일의 명시적 형변환
    cout << n << ", " << d << endl;
}
  1. 10, 3.5
    3, 3.5
    3, 3.5
    3, 3.5

 정수와 실수의 기본 타입인 int와 double 사이의 형변환입니다.

 

void*의 형변환에 사용합니다.

#include <iostream>
using namespace std;

void main( )
{
    int n = 10;
    void *pv = &n;

    cout << *(int*)pv << endl; // C 스타일 형변환
    cout << *static_cast<int*>(pv) << endl; // C++ 스타일 형변환
}
  1. 10
    10

 

int*를 double*로 형변환은 불가능합니다.

#include <iostream>
using namespace std;

void main( )
{
    double *pd = 0;
    int n = 10;
   
    pd = (double*)&n; // C스타일의 형변환 가능
    pd = static_cast<double*>(&n); // C++ 스타일의 형변환 불가능 에러~~
}

  1. 에러~

int*를 double*로 변환은 의미 없는 변환이므로 static_cast는 잘못된 형변환으로 컴파일러가 인식합니다.

 

부모형을 자식형으로 형변환에 사용합니다.

#include <iostream>
using namespace std;
class Parent
{
};
class Child : public Parent
{
};
void main( )
{
    Parent *p = new Child;

    (Child*) p; // C 스타일 형변환
    static_cast<Child*>(p); // C++ 스타일 형변환
}
  1. 출력 없음.

 당연히 가능합니다.

 

또 하나 부모 형식을 자식 형식으로 형변환합니다. <= 잘못된 형변환 예제입니다.

#include <iostream>
using namespace std;

class Parent
{
};
class Child1 : public Parent
{
};
class Child2 : public Parent
{
};
void main( )
{
    Parent *p = new Child1;

    (Child2*) p; // C 스타일 형변환
    static_cast<Child2*>(p); // C++ 스타일 형변환
}
  1. 출력 없음.

 p는 Child1객체를 가지고 있지만 형변환을 Child2로 하고 있습니다. 컴파일 시간에는 p가 어떤 객체를 가지고 있는지 알지 못하므로 static_cast를 사용하면 위 내용은 문제 없이 컴파일됩니다. 그래서 버그가 만들어 지는 것이지요. 위 내용은 danamic_cast를 사용하여 해결할 수 있습니다.

 

2, const_cast 연산자

const_cast는 const를 비const화 할 때 사용합니다. 비 const화 할 수 있는 능력의 형변환은 const_cast 뿐입니다.

 

const 객체를 비 const 객체로 변환하는 예제입니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void Print( )
    {  
        cout << x << ", " << y << endl;
    }
};

void PrintPoint(const Point & arg)
{

    // arg.Print() <= 형변환하지 않으면 호출할 수 없습니다. 멤버 함수 Print()가 const함수가 아니므로...
    const_cast<Point&>(arg).Print( ); // <= arg를 비 const 객체로 변환하여 호출합니다.
}
void main( )
{
    Point p1(2,3);

    PrintPoint( p1 );
}

  1. 2, 3

 const 객체인 arg를 비 const 객체로 변환하여 함수를 호출합니다. 위 코드는 예제를 보이기 위함으로 Print()함수는 당연히 const 함수여야 합니다.

만약 소스 코드를 수정할 수 없는 경우나 라이브러리 내에 들어있는 함수가 비 const 함수이고 const 객체로 함수를 호출해야한다면 const_cast를 사용해야 합니다.

 

const 포인터를 비 const 포인터로 변환하는 예제입니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void Print( )
    {   
        cout << x << ", " << y << endl;
    }
};

void PrintPoint(const Point *pArg)
{
    const_cast<Point*>(pArg)->Print( ); // pArg를 비 const로 변환합니다.
}
void main( )
{
    Point p1(2,3);

    PrintPoint( &p1 );
}
  1. 2, 3

 위 예제와 같습니다. 패~스!

 

3, reinterpret_cast 연산자

reinterpret_cast는 구현 환경마다 다르게 동작하고 C 스타일의 형변환처럼 무식한 형변환 연산자입니다.

 

아래는 무식한 예제입니다.

#include <iostream>
using namespace std;

class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void Print( )
    {  
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    int n = 10;

    reinterpret_cast<Point&>(n); // 오~~ 가능..
    reinterpret_cast<Point*>(&n); // 오오~~ 가능..
    reinterpret_cast<Point&>(n).Print();// 오오오~~ 가능..
    reinterpret_cast<Point*>(n)->Print();// 오오오오~~ 가능.. ㅡㅡ;
}

  1.  
  2.  ㅡㅡ;

 설명이 필요 없습니다. 할말이 없다는...

 

그렇다면 reinterpret_cast 연산자는 어디에 쓰는 것일까요? 쓰지 않는게 좋지만 꼭 써야하는 경우도 있습니다.

#include <iostream>
using namespace std;
void main( )
{
    int n = 0x44434241;
    char *pc = 0;

    pc = reinterpret_cast<char*>(&n);
    cout << pc[0] << endl;

    cout << pc[1] << endl;
    cout << pc[2] << endl;
    cout << pc[3] << endl;
}

  1. A
    B
    C
    D

 명확히 강제 형변환을 의도했을 때입니다. 위 예제처럼 정수는 byte 단위로 접근한다든지, 네트워크 패킷의 임의 테이터에 접근하는 경우 등입니다.

 

4, dynamic_cast 연산자

 dynamic_cast는 실시간에 형검사를 하거나 형변환할 때 사용합니다.

 

다형성을 지원하기 위해 부모 형식을 자식 형식으로 다운 캐스트하고 형식을 검사합니다. 

#include <iostream>
using namespace std;

class Parent
{
public:
    virtual void Print( ) = 0;
};
class Child1 : public Parent
{
public:
    void Print( ) { cout << "class Child1" << endl; }
};
class Child2 : public Parent
{
public:
    void Print( ) { cout << "class Child2" << endl; }
};
void main( )
{
    Parent *p = new Child1;

    Child1 *pChild1 = dynamic_cast<Child1*>(p); 
    if( NULL == pChild1 )
        cout << "Child1 객체가 아닙니다." <<endl;
    else
        pChild1->Print();

    Child2 *pChild2 = dynamic_cast<Child2*>(p);
    if( NULL == pChild2 )
        cout << "Child2 객체가 아닙니다." <<endl;
    else
        pChild2->Print();
}
  1. class Child1
    Child2 객체가 아닙니다.

 dynamic_cast는 실행 시간에 형변환하여 형변환이 불가능하면 NULL을 반환하고 가능하면 변환합니다. 주의할 점은 다형적인 객체에만(virtual 함수를 포함하는 클래스 객체) dynamic_cast를 사용할 수 있습니다. 당연한 이유로 dynamic_cast는 실행 시간에 정확한 객체 형식을 구분하여 인터페이스를 사용하기 위한(멤버 함수를 호출하기 위한) 목적으로 사용되기 때문입니다.

 

레퍼런스 예제입니다.

#include <iostream>
using namespace std;
class Parent
{
public:
    virtual void Print( ) = 0;
};
class Child1 : public Parent
{
public:
    void Print( ) { cout << "class Child1" << endl; }
};
class Child2 : public Parent
{
public:
    void Print( ) { cout << "class Child2" << endl; }
};
void main( )
{
    Child1 child;
    Parent &parent = child;

    try{
        dynamic_cast<Child1&>(parent).Print();
    }
    catch( bad_cast &e)
    {
        cout << e.what() << endl;
    }

    try{
        dynamic_cast<Child2&>(parent).Print();
    }
    catch( bad_cast &e)
    {
        cout << e.what() << endl;
    }
}
  1. class Child1
    Bad dynamic_cast!

 dynamic_cast는 실행 시간에 형변환하여 형변환이 불가능하면 bad_cast을 throw하고 가능하면 변환합니다.

 

 

'Windows Developer > C++' 카테고리의 다른 글

[C++]STL에 대해서(보충)  (0) 2010.08.01
[C++] 초기화 리스트(Initialize List)  (1) 2010.07.26
[C++] 가상함수  (0) 2010.07.26
[C++]템플릿(template)  (0) 2010.07.20
[C++]STL 표준 C++ 라이브러리  (0) 2010.07.20