-
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>(표현식) 암시적 변환을 강제로 진행할 때 사용
신형 스타일의 캐스트는 다음과 같은 장점이 있다.
- 코드를 읽을 때 알아보기 쉽기 때문에 타입 시스템이 망가졌을 때 디버깅이 쉽다.
- 목적을 좁혀서 캐스팅하기 때문에 컴파일러에서 에러를 체크할 수 있다.
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를 쓰기 적절해 보인다면 뭔가 잘못되가고 있는 것이다. 코드를 다시 확인하자.
- 구형 스타일 캐스트 C 스타일