[c++] if문 사용 줄이기
글쓴이: skek2875 / 작성시간: 목, 2018/07/05 - 1:52오후
안녕하세요. if문을 줄이려고 하는 도중에 막히는 부분이 생겨서 질문 드립니다.
void Proj::Func1(int type) { if (type == A) A_test(); else if (type == B) B_test(); else if (type == C) C_test(); else if (type == D) D_test(); } void Proj::Func2(int type) { if (type == A) A_print(); else if (type == B) B_print(); else if (type == C) C_print(); else if (type == D) D_print(); }
Proj 내부의 모든 함수에는 위 예의 Func1, Func2와 같이 type을 비교해야하는 if문이 있는 상태입니다.
이런 if문을 줄이려고 A, B, C, D 각각 클래스로 분리시켜서 전달인자로 type만 넘겨주면 알아서 해당 type의 클래스를 호출하도록 만들고 싶습니다.
void* CallFunc(int type);
위와 같이 선언된 함수를 Func1, Func2 함수가 호출하면,
void Proj::Func1(int type) { (CallFunc(type))->test(); } void Proj::Func2(int type) { (CallFunc(type))->print(); }
위와 같은 형태가 되고 각 클래스의 멤버함수를 호출하도록 만들면 되겠구나 생각했습니다.
문제는 CallFunc함수의 구현인데요...
일단 다음과 같이 구현했습니다.
void* Proj::CallFunc(int type) { if (type == A) return m_pA; else if (type == B) return m_pB; else if (type == C) return m_pC; else if (type == D) return m_pD; }
이렇게 단순히 각 클래스에 대한 객체의 포인터를 return하면 그 객체의 멤버함수를 호출할 수 있을 것이라고 생각했는데 위와 같이 코딩해도 결국에는 Func1, Func2에서 CallFunc를 원하는 클래스로 형변환 시켜야 하더라구요...
아직 기초가 많이 부족해서 그러는데 이를 해결할 수 있는 방법이 있을까요?
Forums:
그렇게 할 수 있는 방법은 많죠. 함수 포인터를
그렇게 할 수 있는 방법은 많죠. 함수 포인터를 활용한다던가.
보아하니 C++같은데 C++답게 다형성과 가상 함수로 해결합시다.
이렇게!
추신
추신
C++를 쓰고 있다면, 뭔가 굉장히 로우레벨적인 일(운영체제로부터 raw memory를 할당받아서 사용한다던가)을 하는 상황이 아닌 한, 코드에
void *
가 나타났다는 건 별로 좋은 상황이 아닙니다.뭔가 말도 안 되는 일을 시도하고 있는 게 아닐까 자문하시고, 언어 차원에서 권장되는 더 바람직한 방법을 찾아야 합니다.
감사합니다!
답변 덕분에 문제를 해결해나가고 있는 중입니다 ㅎㅎ
직접 예제까지 올려주셔서 설명해주셔서 감사합니다.
좀 의아한데..
우선 runtime의 binding을 이용하시려면, int type 대신에 print/test를 override하는 객체 자체의 포인터를 넘기는 게 좋습니다. 그게 안 되고 있는 것이 문제의 시작인 것 같네요. c++은 runtime이든 compile time이든 binding을 할 때 parameter의 type으로 뭐가 뭔지 구별을 하게 되는 셈인데 지금은 int 타입만 들어가니까 switch-case, if, 혹은 table look-up 중 하나는 피하기 어려워요.
가능하면 int type을 넘기는 대신, 예컨대 익명님의 예에서는 Func 자체를 넘길 수 있도록 디자인을 수정해 보시는 게 한 방법이라고 생각합니다.
굳이 int type을 넘기셔야 한다면, switch는 피할 수 없습니다. 이 경우에는 dynamic binding은 좀 오버킬이라는 생각이 드네요. 그냥 if-else를 switch로 바꾸는 것으로도 충분하다고 생각합니다.
Func를 넘길 수 있도록 디자인을 수정하는 건
Func를 넘길 수 있도록 디자인을 수정하는 건 좋은데... 그럼 Func 객체는 누가 만들죠?
type이 사용자나 네트워크, 혹은 스토리지로부터 오는 값이라면, 결국 어디선가 int type 값을 Func class hierarchy로 옮겨 주는 코드가 필요할 것입니다. 꼭 dynamic binding이 아니라고 해도 비슷한 역할의 코드가 있어야 하는 건 마찬가지겠지요. 저는 질문에서의 CallFunc 함수 아이디어를 좀 더 구체화시켰을 뿐입니다. (근데 왜 static method로 만들지 않았었는지는 모르겠네요. 깜빡한 것 같습니다.)
it-else/switch-case/table lookup 중 어느 것을 쓰는지는 크게 중요할 것 같지 않는데, 제 경험상 Func1, Func2 혹은 다른 곳에서 각각 따로 switch를 쓰는 것보다는, 차라리 CallFunc 안에 스위치 하나를 만들어 두고 가상함수나 함수 포인터 같은 방법으로 해결해버리는 편이 좋은 것 같습니다. 코드 중복은 언제든 좋지 않으니까요. 특히 나중에 case가 추가될 수 있다고 하면 고쳐야 하는 곳을 최대한 줄이는 게 좋지 않겠어요?
네..
질문 자체에 이 코드가 들어갈 맥락이 거의 안 나와 있으니 별로 생산적인 논쟁이 될 것 같아 보이진 않습니다.
제 얘기는 "int type을 사용하는 것을 피할 수 있다면" 코드가 더 간결해질 거라는 얘기였고, 없다면 어차피 switch-case, if 또는 table look-up 같은 것을 피할 수 없다는 거였죠. 어딘가에서 type을 int나 enum 같은 걸로 꼭 써야 하는 상황이거나 안 쓰자면 개발 비용이 지나치게 추가되어야 한다면, 말씀하신 대로 "비슷한 역할을 하는 코드가 있어야 하는 건 마찬가지"겠지요.
if-else, switch-case, 그리고 table look up의 경우 컴파일러 최적화와 생성된 코드의 성능 차이는 조금 있긴 있겠습니다. 그러나 이 예제만 보면, 어딘가 인라인 되어 핫 룹에 돌지 않는 한 그리 큰 차이는 아니겠고요. 그보다 저는 개인적으로 연쇄적인 if-else를 보는 게 이상하게 괴로워요. 질문도 if-else를 피하자면 어떻게 하냐는 거였으니 제 답변은 이 정도로 간단한 예라면 그냥 switch-case를 쓰시면 되지 않겠냐는 거죠.
마지막 단락은 동의하는데요, 마찬가지로 질문하신 분이 이 코드를 사용하게 될 맥락에 따라 달라질 문제라고 생각합니다. 별로 변화가 없고 메써드도 몇 개 되지 않는다면 오버킬일 수도 있고, 말씀대로 소프트웨어 라이프 사이클에서 자주 변하고 이런 방식으로 사용할 메써드가 많다면 그렇지 않겠지요. 제가 오버킬 아니냐고 한 건, 질문 속의 예제가 매우 단순한 데 비해서 답변의 코드는 상당히 길어서 꺼낸 얘기였습니다.
댓글 달기