[ Effective C++ ] 정리 모음집
" C++ 프로그래머의 필독서, 스콧 마이어스의 Effective C++ 를 읽고 내용 요약 / 정리 "
" 캐스트는 온갖 골칫거리를 다 몰고 다니는 풍운아! "
- 다른 방법이 가능하다면 캐스팅은 피하자! 특히 수행 성능에 민감한 코드에서 dynamic_cast는 몇번이고 다시 생각해보고, 설계 중 캐스팅이 필요해 졌다면 캐스팅을 쓰지않는 방법을 시도해 보자!
- 캐스팅이 어쩔 수 없이 필요하다면, 사용자가 자신의 코드에 캐스팅을 넣지 않고 함수를 호출할 수 있게 함수 안에 숨길 수 있도록 하자!
- 구형 스타일의 캐스트 보다는 C++ 스타일의 캐스트를 쓰자! 발견하기도 쉽고, 설계자가 어떤 역할을 의도했는지가 더 자세히 드러난다!
(int)fNumber;
int(fNumber);
'안전한 다운캐스팅'을 할 때 사용
- 주어진 객체가 어떤 클래스 상속 계통에 속한 타입인지 아닌지 결정
구형 스타일 캐스트 문법으로는 흉내조차 낼 수 없음
런타임 비용이 높음
포인터를 int로 바꾸는 등의 하부 수준 캐스팅을 할 때 사용
- 하부 수준 코드 외에는 사용하지 말아야 함
적용 결과가 구현 환경에 의존적
비상수 객체를 상수 객체로 바꾸거나, int를 double로 바꾸는 등의 암시적 변환을 강제로 진행할 때 사용
타입 변환을 거꾸로 수행하는 용도로도 쓰임
- void*를 일반 타입의 포인터로, 기본 클래스 포인터를 파생 클래스 포인터로 바꾸는 등
- 상수 객체를 비상수 객체로 바꾸는 건 불가능, 해당 기능은 const_cast 만이 수행 가능
📢 구형 스타일 캐스트를 쓰는 경우!
class Widget
{
public:
explicit Widget(int size);
...
};
void doSomeWork(const Widget& w);
doSomeWork(Widget(15)); // 구형 스타일 캐스트 사용
doSomeWork(static_cast<Widget>(15)); // 신형 스타일 캐스트 사용
class Window
{
public:
virtual void onResize() { ... }
...
};
class SpecialWindow : public Window
{
public:
virtual void onResize()
{
static_cast<Window>(*this).onResize();
...
}
...
};
📢 해결 방법
class SpecialWindow : public Window
{
public:
virtual void onResize()
{
Window::onResize();
...
}
...
};
어떤 구현환경에서는 클래스 이름에 대한 문자열 비교 연산에 기반을 두어 dynamic_cast가 만들졌다
- 깊이가 4인 상속 계통에서 strcmp가 4번 불린다, 깊이가 더 깊거나, 다중 상속이면...?
대부분의 구현환경의 경우 동작 속도 측면만 생각해도 무방하다!
- 그래도 수행 성능에 사활이 걸린 코드라면 특히 dynamic_cast에 주의를 놓지 말자!
파생 클래스 객체임이 분명한 객체에 대해 파생 클래스 함수를 호출 하고 싶은데, 조작할 수 있는 수단이 기본 클래스의 포인터, 참조자 밖에 없을 때...
class Window { ... };
class SpecialWindow : public Window
{
public:
void blink();
...
};
typedef
vector<shared_ptr<SpecialWindow>> VPSW;
VPSW winPtrs;
...
for (VPSW::iterator iter = winPtrs.begi(); iter != winPtrs.end(); ++iter)
{
(*iter)->blink();
}
- 해당 방법은 파생 클래스 모두를 위한 각각의 컨테이너가 만들어져야 하는 단점이 있다.
class Window
{
public:
virtual void blink() {} // 구현 부를 비워둔다.
};
class SpecialWindow : public Window
{
public:
virtual void blink();
...
};
typedef
vector<shared_ptr<Window>> VPW;
VPW winPtrs;
...
for (VPSW::iterator iter = winPtrs.begi(); iter != winPtrs.end(); ++iter)
{
(*iter)->blink();
}
📢 두 방법 모두 어떤 상황이든 간에 다 적용하긴 불가능 하지만 알아만 둔다면 적용 할 수 있는 상황에 많은 도움이 된다!
class Window { ... };
...
typedef
vector<shared_ptr<Window>> VPW;
VPW winPtrs;
...
for (VPSW::iterator iter = winPtrs.begi(); iter != winPtrs.end(); ++iter)
{
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()))
{
...
}
...
}
코드 블럭이 크기만 하고, 속도도 둔할 뿐더러 망가지기 쉬운 코드
클래스의 상속 계통이 조금이라도 바뀔 때 마다 검토 및 수정이 필요해짐