ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Effective C++...25
    일지 2022. 7. 15. 09:03

    캐스팅은 절약, 또 절약! 잊지 말자

    C++의 캐스팅 방법

    C++ 에는 C 버전의 구형 스타일 캐스트와 C++ 버전의 신형 스타일 캐스트가 존재한다.

    • 구형 스타일 캐스트 C 스타일
      • (T) 표현식
      • T(표현식)
    • 신형 스타일 캐스트 C++ 스타일
      • const_cast<T>(표현식) 객체의 상수성을 없애는 용도
      • dynamic_cast<T>(표현식) 안전한 다운 캐스팅에 사용, 런타임 비용이 굉장히 높다.
      • reinterpret_cast<T>(표현식) 하위 수준 캐스팅에 사용, 일반적으로 사용되지 않는다.
      • static_cast<T>(표현식) 암시적 변환을 강제로 진행할 때 사용

     

    신형 스타일의 캐스트는 다음과 같은 장점이 있다.

    1. 코드를 읽을 때 알아보기 쉽기 때문에 타입 시스템이 망가졌을 때 디버깅이 쉽다.
    2. 목적을 좁혀서 캐스팅하기 때문에 컴파일러에서 에러를 체크할 수 있다.

     

    C++에서 캐스트를 조심해야 하는 이유

    C++를 쓸 때는 객체가 가질 수 있는 주소가 하나가 아닐 수도 있다.

     

    다음 코드는 Derived*가 가리키는 주소와 Base*일 때 가리키는 주소가 다를 수 있음을 보여준다.


    class Base { ... };

    class Derived: public Base { ... };

     

    Derived d;

    Base *pb = &d; // Derived* 에서 Base*의 암시적 변환이 이루어진다.


     

    ※ C++를 쓸 때는 데이터가 어떤 식으로 메모리에 박혀 있을 거라는 섣부른 가정을 피해야 한다.

     

    dynamic_cast가 느린 이유

    특정 컴파일러에서는 클래스 이름에 대한 문자열 비교 연산에 기반을 두어 만들어졌다. 따라서 깊이가 깊어질수록 더 느려지게 된다.

     

    정말 피해야 하는 설계로 폭포식 dynamic_cast라는 구조가 있는데 다음과 같은 구조를 말한다.


    class Window { . . .};

    ... // 파생 클래스 정의

    typedef std::vector<std::tr1::shared_ptr<window>> VPW;

    VPW winPtrs;

    ...

    for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)

    {

        // Window 클래스가 바뀌거나 파생 클래스가 생겼을 때마다 관리를 해 줘야 하고 느리기까지 하다.

        if (SpecialWindow1 *psw1 = dynamic_cast<SpecialWindow1*>(iter->get())) { ... }

        else if (SpecialWindow2 *psw2 = dynamic_cast<SpecialWindow2*>(iter->get())) { ... }

        else if (SpecialWindow3 *psw3 = dynamic_cast<SpecialWindow3*>(iter->get())) { ... }

    }


     

    dynamic_cast를 피하는 방법

    두 가지 방법으로 dynamic_cast를 줄일 수 있다.

    • 파생 클래스의 포인터를 컨테이너에 담아 사용하는 방법
    • 가상 함수를 만들어 필요한 구현이 자연스럽게 호출되도록 하는 방법

     

    ※ dynamic_cast를 쓰기 적절해 보인다면 뭔가 잘못되가고 있는 것이다. 코드를 다시 확인하자.

     

    댓글

Designed by Tistory.