C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[391] C++에 추가된 cast 연산자들
홍환민.행복 [hhshhm] 10001 읽음    2003-04-21 20:14
안녕하세요? 환민입니다.

C++에 추가된 cast 연산자들에 대한 좋은 글이 있어 퍼 왔습니다.

뉴스그룹의 "김승범" 님의 글입니다.
//----------------------------------------------------------------------------------------

캐스트 연산은 주어진 식이 가지고 있는 형을 다른 형으로 강제로 바꾸는
것입니다. C++에는 (C 시절부터 존재하는 C 스타일 캐스트를 제외하고)
다음 네 가지 종류의 캐스트 연산이 있습니다.

퍼온이 주)
(type)variable 형태의 기존의 C 스타일 캐스트 연산자가 있고,
type(variable) 형태의 C++에 새로 추가된 캐스트 연산자가 있습니다.
둘은 동일한 기능을 하며, 두번째 경우는 함수 호출과 같은 형태로 형변환을 할 수
있다는 장점이 있습니다.(보기 좋다. 사용하기 편할 때가 가끔 있다.. 정도..)


* dynamic_cast
* static_cast
* reinterpret_cast
* const_cast

---------------
1. dynamic_cast
---------------

dynamic_cast<T>(e)는 부모 클래스와 자식 클래스의 관계에 있는
포인터 형 사이의 변환 또는 레퍼런스 형 사이의 변환을 수행하는데,

- 같은 형 사이의 변환
- 널 포인터의 변환
- 자식 클래스로부터 부모 클래스로의 변환

과 같은 '뻔한' 경우가 아니라면 e는 다형적 형(polymorphic type;
가상 함수가 포함된 클래스 형)의 좌변값이나 포인터여야 하며,
컴파일시에 변환이 이루어지는 다른 종류의 캐스트 연산과는 달리
실행시에 동적 형(dynamic type)에 근거한 변환이 시도되고,
변환의 성공 여부를 검사하는 의미도 함께 가지고 있습니다.

포인터의 경우 변환이 실패하면 결과값은 널 포인터가 되는데,
이를 if 등의 조건 검사에 활용할 수 있습니다.

    struct animal { virtual void ~animal(); };
    struct dog : animal { void bark(); };
    struct cat : animal { void mew(); };

    void test(animal* a)
    {
        if (dog* d = dynamic_cast<dog*>(a)) d->bark(); else
        if (cat* c = dynamic_cast<cat*>(a)) c->mew();
    }

레퍼런스의 경우 변환이 실패하면 <typeinfo> 헤더에 정의되어 있는
std::bad_cast 예외가 발생합니다. 즉 이는 주어진 변환이 성공할 것을
알고 있을 때 주로 사용합니다.

    void test(animal& a)
    {
        dog& d = dynamic_cast<dog&>(a);
        d.bark();
    }

    dog d; test(d);  // 성공
    cat c; test(c);  // 실패 - std::bad_cast 예외 발생

dynamic_cast와 비슷한 성질을 가지고 있으면서 변환 대신 형 검사만 하는
typeid 연산자도 있는데, 피연산자는 식이나 형이 되고, 연산의 결과값은
<typeinfo> 헤더에 정의되어 있는 std::type_info 형의 좌변값입니다.

    void test_equal(animal& x, animal& y)
    {
        if (typeid(x) == typeid(y)) { /* 같은 종류 */ }
        else { /* 다른 종류 */ }
    }

    void test_dog(animal& x)
    {
        if (typeid(x) == typeid(dog)) { /* x is a dog */ }
    }

dynamic_cast와 typeid는 C++에서 제공하는 실행시의 형 정보(RTTI;
run-time type information)의 일환인데, 이를 남발하면 클래스 체계를
확장하고 관리하기가 어려워지므로 꼭 필요한 경우에만 사용하고
되도록 가상 함수를 통한 다형성을 이용하는 것이 바람직합니다.

--------------
2. static_cast
--------------

static_cast<T>(e)는 가장 일반적인 형태의 캐스트 연산으로, 어떤 임시
변수 t를 T t(e);와 같이 선언하고 초기화하여 그 임시 변수 t의 값을
사용하는 것과 같은 암시적인 변환을 비롯하여, 산술형(char, int, double
등) 및 열거형(enum) 사이의 변환, 부모 클래스와 자식 클래스의 관계가
관련된 변환, void 형으로의 변환 등을 수행할 수 있습니다. 다만 부모
클래스와 자식 클래스 사이의 변환은 주어진 식의 동적 자료형(dynamic
type)이 아닌 정적 자료형(static type)에 전적으로 의존합니다.

    inline int integer_quotient(double a, double b)
    { return static_cast<int>(a / b); }

    animal* a = new dog;
    dog* d = static_cast<dog*>(a);  // 올바른 캐스트 연산

    animal* a = new cat;
    dog* d = static_cast<dog*>(a);  // 잘못된 캐스트 연산

    animal* a = new animal;
    dog* d = static_cast<dog*>(a);  // 잘못된 캐스트 연산

-------------------
3. reinterpret_cast
-------------------

reinterpret_cast<T>(e)는 서로 다른 형의 포인터 사이의 변환이나,
정수와 포인터 사이의 변환 등 서로 관계가 없는 형 사이의 변환을,
구현체가 정의하는 방법에 따라 수행합니다. 정수와 포인터 사이의
변환의 결과값은 주로 e를 표현하는 비트열을 그대로 정수 및 포인터로
해석한 값이 됩니다.

reinterpret_cast는 그 변환 방법이 대부분 구현체가 정의하도록
맡겨져 있어서 이식성을 떨어뜨리며, 요구된 변환이 올바른 변환인지의
여부를 검사하지 않으므로 신중하게 사용해야 합니다.

퍼온이 주) '구현체'란 각 컴파일러를 뜻합니다. 즉, 각 컴파일러마다
동작 방식이 다를 수 있다는 것이지요.

    unsigned char* const video_base =
        reinterpret_cast<unsigned char*>(0x80000000);

    unsigned int ui = 0x01234567;
    *reinterpret_cast<unsigned char*>(&ui) = 0xFF;

-------------
4. const_cast
-------------

const_cast<T>(e)는 const 또는 volatile으로 한정된 형에서 이들을
떼어내는 변환을 수행할 수 있습니다. 이는 C++ 형 체계를 무너뜨릴 수
있으므로 신중하게 사용해야 합니다.

퍼온이 주) 쉽게 다시 말하면 const_cast를 사용하면 const 변수는
non-const 변수로 만들 수 있고, volatile 변수는 non-volatile 변수로
만들 수 있다는 말입니다.

    void lie(const int* pci)
    {
        int* pi = const_cast<int*>(pci);
        *pi = 0;
    }

    int i = 1;
    lie(&i);           // OK

    const int ci = 1;
    lie(&ci);          // Ouch!!


위 네 가지 중에서 dynamic_cast를 제외한 셋은 C에서 (type)expression
의 형태로 사용할 수 있었던 것인데, C++에서도 "C 스타일 캐스트 연산"
이라고 불리며 남아 있기는 합니다만, 새로 작성하는 C++ 코드에서는
C++ 스타일의 캐스트 연산을 사용하는 것이 좋습니다. 이는 어떤 종류의
변환을 프로그래머가 의도하는지 명확하게 나타내 주며, 위험할 수 있는
캐스트 연산이 코드에서 좀 더 두드러져 보이도록 하고 찾기도 쉽게
만들어주기 때문입니다.

위의 내용은 캐스트 연산에 대한 일반적인 설명인데, 좀 더 구체적인
상황에서의 적용 예를 보고 연습을 해 보시려면 GotW #17을 참조해
보시기 바랍니다. <http://www.gotw.ca/gotw/017.htm>


//----------------------------------------------------------------------------------------

아래 글도, 도움이 될만한 또다른 설명입니다.
데브피아 Visual C++ Q&A란의 "이원구" 님의 글이 도움이 될 것 같아
퍼왔습니다.

//----------------------------------------------------------------------------------------

안녕하세요. 이원구입니다.

기존의 C에 있던 cast 연산에는 몇가지 문제가 있었습니다.
예를 들어 다음과 같은 문장을 보면

Data* data = (Data*)pSomething;

여기서 쓰인 cast가 무엇인지 알수 있는 방법이 없습니다. 코드를 보는 사람뿐 아니
라 컴파일러도 알수 없겠죠. 따라서 컴파일러는 무조건 casting을 하게 됩니다. 또
한 후에 아주 긴 코드상에서 cast한 부분만을 보고자 할때도 검색어로 괄호를 넣을수
도 없고 난감하지요.

이러한 몇가지 점을 보완하고 개발자들에게 여러가지 편리함을 주고자 C++에서는 네
가지 cast operator가 정의되었습니다. static_cast, dynamic_cast,
const_cast, reinterpret_cast가 그것입니다.

간단히 설명을 하자면 다음과 같습니다.

static_cast - 기본적으로 기존 C에서 casting하던 것과 같은 역할을 합니다. 따
라서 기존의 C의 casting으로 안되던 것들은 이것으로도 되지 않습니다. 또한
const형을 non-const형으로 cast하는 것 역시 이 연산자로 할수 없습니다.

const_cast - const형 데이터를 non-const형으로 바꾸거나 volatile형을 non-
volatile형으로 cast해 줍니다. 다른 용도로는 사용할 수 없습니다.

dynamic_cast - class들의 inheritance hierachy들 사이를 올바르게 cast하는
데 사용됩니다. 다른 용도로는 사용할수 없습니다. 이 기능은 RTTI(Run-time
Type Information)기능을 참고하시면 도움이 되실 겁니다. 참고로 이 연산자는 올
바로 cast되지 않는 경우에는 null을 return하기 때문에 매우 유용하게 사용됩니
다.

reinterpret_cast - 이 cast는 보통 구현에 따라 다르게 작업을 수행한다고 되어
있습니다. 따라서 이 연산자는 다른 컴파일러에 porting이 어렵다고 되어 있습니
다. 보통 위의 세가지 cast로 불가능한 casting을 할때 사용되며 서로 다른 프로토
타입을 가진 함수 포인터를 casting하는데 주로 사용이 됩니다.

다시 위의 예로 돌아가면

Data* data = const_cast<  Data*>(pSomething);
Data* data = dynamic_cast<  Data*>(pSomething);
Data* data = static_cast<  Data*>(pSomething);

와 같은 문장이 있다면 왜 casting이 필요했는지를 쉽게 이 부분의 코드만 봐도 확인
이 되겠지요. 물론 컴파일러도 좀더 효율적이고 정확하게 컴파일이 가능할테고요.

마지막으로 C++에 관련된 책중에 여러가지 item으로 구성되어 있어서 재밌고 유용하
게 읽을수 있는 책으로

Effective C++, by Scott Meyers, Addison Wesley
More Effective C++, by Scott Meyers, Addison Wesley

가 있습니다. 꼭 한번 읽어 보세요. 그냥 C++책이나 object-oriented 관련 서적
을 읽는 것과는 다른 맛(?)이 있습니다.

그럼 이만...

도움이 되셨나요.

:) iwongu /R /C /S /L
mailto:iwongu@rcsl.inha.ac.kr
http://rcsl.inha.ac.kr/~iwongu

//----------------------------------------------------------------------------------------


+ -

관련 글 리스트
391 C++에 추가된 cast 연산자들 홍환민.행복 10001 2003/04/21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.