4 분 소요

모던 C++

개요

조건식에 따라 분기하거나 반복하는 제어문들을 제공합니다.

단순한 조건 검사에서 복잡한 로직 구현까지 두루두루 제어문이 사용되는데요, STL 에서는 시퀀스 처리, 정렬, 검색, 수학 등 일반화된 알고리즘들을 제공합니다. 한번 훑어보시고, 필요한 것이 있다면 알고리즘을 활용하시는 게 유지보수 측면에서 좋습니다.

항목 내용
if (조건) {표현식;} 조건이 참일 경우 표현식을 실행
if (조건1) {표현식1;}
else if (조건2) {표현식2;}
else {표현식3;}
조건1이 참일 경우 표현식1을 실행하고, 거짓일 경우 조건2가 참일 경우 표현식 2를 실행하고, 거짓일 경우 표현식3을 실행
switch(조건) {
case 상수1:
       표현식;
       break;
case 상수2:
       표현식;
       break;
default:
       표현식;
       break;
}
조건과 매칭되는 case 문 으로 분기.
* 조건 : 정수 또는 열거형
* break : 해당 case 표현식을 실행하고 switch문 탈출
* default : 매칭되는 case문이 없는 경우 실행
while (조건) {표현식;} 조건이 참이면 표현식 반복 실행
do {표현식;} while(조건); 표현식 실행후 조건 검사 하여, 조건이 참이면 표현식 반복 실행
for (초기식; 조건식; 표현식2;) {표현식1;} 초기식;
while (조건식) {
       표현식1;
       표현식2;
}
의 순서로 반복
continue; 반복문의 범위 끝으로 이동하여 다음 조건 검사
break; 반복문 탈출
return; 함수 종료
goto LABEL; 지정한 LABEL로 강제 이동(LABEL:와 같이 콜론으로 표기함)
코드 분석을 방해하기 때문에 비권장

(C++17~) 초기식을 포함하는 if(), switch()가 추가되어 함수 리턴값을 평가하고 소멸하는 코드가 단순해 졌습니다.

중첩된 제어문

제어문이 중첩되면, 코드 분석이 어려워 집니다.

1
2
3
4
5
6
7
if (a) {
    if (b) {
        if (c) {
            f(); // (△) 비권장. 분석하기 어렵습니다.
        }
    }
}

보다는

1
2
3
if (a && b && c) {
    f();
}

가 낫습니다.

제어 판단과 실행의 구분(조건 상태표)

복잡한 제어 구조를 가진 경우, 상태를 판단하는 역할(조건 상태표 기준으로 계산)과 실행하는 역할을 분리하면, 구조가 간결해 질 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (a && b) { // (△) 비권장. if 중첩이 많아 분석하기 힘듭니다.
    if (c || d) {
    }
    else {
        if (b && e) {
        }
        else {
        }
    }
}
else {
    if (c || d) {
        if (e || f) {
        }
        else {
        }
    }
    else {
    }
}

보다는

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
State CalcState() {
    if (a && b && (c || d)) {return State::A;}
    if (a && b && !(c || d) && (b && e)) {return State::B;}
    if (a && b && !(c || d) && !(b && e)) {return State::C;}
  
    if (!(a && b) && (c || d) && (e || f)) {return State::D;}
    if (!(a && b) && (c || d) && !(e || f)) {return State::E;}
    if (!(a && b) && !(c || d)) {return State::F;}

    assert(false);
}

State state = CalcState(); // 조건 상태표에 따라 상태를 리턴합니다.

switch (state) {
    case State::A: break;
    case State::B: break;
    case State::C: break;
    case State::D: break;
    case State::E: break;
    case State::F: break;
    default: assert(false); break;
}

가 낫습니다.

if와 else

ifelse 상황을 동반할 때가 많습니다. 단순한 널검사인 경우는 상관없습니다만, 복잡한 상황일 경우 else 인 경우의 처리가 정말 필요 없어서 생략한 건지, 실수로 빼먹은 건지 혼동될 수 있으니, else 상황에 대해 주석으로나마 고려한 흔적을 남기는게 좋습니다.

1
2
if (condition) { // (△) 비권장. else문이 고려되었는지 판단하기 어렵습니다.
} 

보다는

1
2
3
4
5
if (condition) {
}
else {
    assert(false); // (O) "해당 case 절대 없음"
}

가 낫습니다.

사전 조건 검사

함수내에서 본문을 실행하기 전에, 실행하기에 적합한 상태인지 검사(인자 조건은 맞는지 등)한 뒤 본문을 실행하는게 좋습니다. 그렇지 않고 뒤죽박죽 쓰다보면 예외에 안전한 코드를 만들기 어려워 집니다.

1
2
3
4
5
6
7
8
9
10
11
ErrorCode f(param1, param2) {
    if (param1 is OK) {
        표현식1;
    }
    if (param2 is FAIL) {
        return ErrorCode; // (△) 비권장. 이미 실행된 표현식1을 되돌리기 어렵습니다.
    }
    else {
        표현식2;
    }
}

위와 같이 작성한다면, param2가 부적절한 상황이어서 ErrorCode 를 리턴할때 이미 실행된 표현식1을 되돌리기가 어렵습니다.

이렇게 작성하기 보다는,

1
2
3
4
5
6
7
8
9
10
11
12
ErrorCode f(param1, param2) {
    if (param1 is FAIL) {
       return ErrorCode;
    }
    if (param2 is FAIL) {
        return ErrorCode; 
    }

    표현식1;
    표현식2;
    ...
}

가 낫습니다.

일관된 조건 검사

조건의 참/거짓이 일관된 표현으로 작성되는게 좋습니다.

1
2
3
4
if (!condition1) {return;}
if (!condition2) {return;}
if (condition3) {return;} // (△) 비권장. 코딩 실수처럼 보입니다.
if (!condition4) {return;}

보다는

1
2
3
4
if (!condition1) {return;}
if (!condition2) {return;}
if (!condition3) {return;} 
if (!condition4) {return;}

으로 condition3의 참/거짓 논리를 수정하는게 좋습니다.

일관된 대소 비교

대소 비교는 일관된 표현으로 작성하는게 코드 분석에 도움이 됩니다.

1
2
3
4
if (a < b) {
}
if (c > d) { // (△) 비권장. 비교에 일관성이 없어 코드 분석시 헷갈리게 됩니다.
}

보다는

1
2
3
4
if (a < b) {
}
if (d < c) { 
}

가 낫습니다.

댓글남기기