나는 여태까지 C++ 은 정적 컴파일이다보니

동적으로 뭔가를 한다는 게 불가능할 줄 알았는데,


그냥 내가 무식했나보다...ㅎㅎㅎ..



C++ 에서 4 가지 캐스팅 연산자 (dynamic_cast, static_cast, const_cast, reinterpret_cast) 가운데

dynamic_cast 에 대해 알아보고자 한다.



 dynamic_cast 상속 관계 안에서 포인터나 참조자의 타입


기본 클래스 → 파생 클래스 로의 다운캐스팅


다중 상속에서 기본 클래스 간의 안전한 타입 캐스팅에 사용된다.

(안전한 타입 캐스팅 : 런타임에 타입 검사를 하겠다는 의미)


const_cast 와 같이 용도가 명확하기 때문에 다른 용도로는 사용하지 못한다.



+추가로

객체가 위치한 메모리의 시작부분을 찾는 데도 사용된다고 한다.

아래와 같이 p 에 시작주소가 담겨진다.

void* p = dynamic_cast<void*> (ObjectPointer);



* dynamic_cast 의 제약사항

1) 상속 관계 안에서만 사용할 수 있다.

2) 하나 이상의 가상 함수를 가져야 한다.

3) 컴파일러의 RTTI (Runtime Type Information) 설정이 켜져있어야 한다.



[예제1]

class Base {
public:
    virtual void print() { cout << "This is Base" << endl; }
};

class Derived : public Base {
public:
    void print() { cout << "This is Derived" << endl; }
};

void main()
{
    Base* pBase1 = new Base;
    Base* pDerived1 = new Derived;

    Derived* pDerived2 = new Derived;
    Derived* pDerived = nullptr;

    // 컴파일 실패 : 부모는 자식이 될 수 없음
    pDerived = pBase;

    // 컴파일 성공 : but 부모는 자식이 될 수 없으므로 널 포인터를 리턴한다!
    pDerived = dynamic_cast<Derived*> (pBase);
    if ( pDerived == nullptr )
        cout << "failed to casting pBase->pDerived" << endl;


    // 컴파일 실패 : pDerived1 의 타입이 Base* 이므로, 부모는 자식이 될 수 없음
    pDerived = pDerived1

    // 컴파일 성공 : 런타임에 타입 변환이 성공하여 Derived 타입의 포인터를 리턴한다!
    pDerived = dynamic_cast<Derived*> (pDerived1);
    if ( pDerived )
        pDerived->print();


    // 컴파일 성공 : 이런 경우에는 캐스팅이 필요없다.
    pDervied = pDerived2;
}


위 예제에서의 키포인트는 

'포인터가 실제로 가르키는 대상이 캐스팅이 될 수 있는 녀석인가' 이다.



dynamic_cast 는 캐스팅에 실패할 경우,

대상이 pointer 면 nullptr 를 반환하고

대상이 참조자이면 bad_cast 예외를 던진다.


이 점이 위에서 언급한 안전한 타입 캐스팅의 의미이다.

캐스팅의 가능 여부를 런타임에 검사하여 처리할 수있다.




다음은 다중 상속일 때, 기본 클래스 간의 타입 캐스팅(Cross-Casting)을 다룬다.


[예제2]

class BaseOne {
public:
    virtual void print() { cout << "This is BaseOne" << endl; }
};

class BaseTwo {
public:
    virtual void print() { cout << "This is BaseTwo" << endl; }
};

class Derived : public BaseOne, public BaseTwo {
public:
    void print() { cout << "This is Derived" << endl; }
};

void main()
{
    BaseOne* pBaseOne = nullptr;
    BaseTwo* pBaseTwo = new Derived;

    // 컴파일 실패 : BaseOne 과 BaseTwo 타입은 호환 불가
    pBaseOne = pBaseTwo;

    // 컴파일 성공 : 기본 클래스 간의 타입 캐스팅 가능
    pBaseOne = dynamic_cast<BaseOne*> (pBaseTwo);

    if ( pBaseOne )
        pBaseOne->print();
}


[예제2] 를 정리하자면 아래의 그림과 같다.



크로스 캐스팅의 한 사용 예로는 

각 기본 클래스 포인터(or 참조자) 타입의 컨테이너 혹은 배열을 운용하면서 

서로 간의 요소를 교환할 때 사용할 수 있다.




여기까지 dynamic_cast 의 용도를 알아보았다.

( [예제1] : 다운 캐스팅[예제2] : 크로스 캐스팅 )



참고로 다중 상속은 권장하지 않는 분위기(?)라서 설계를 재검토 하도록 하자.




※ 포스팅 참고 자료:

http://prostars.net/55

http://msdn.microsoft.com/ko-kr/library/cby9kycs.aspx



포스팅의 엄청난 도움을 주신 (+게으른 저를 포스팅하게 만들어 주셨습니다)

http://prostars.net/55 님께 감사의 인사를 덧붙입니다. (__)


by kelicia 2014. 10. 26. 23:12