본문 바로가기
C++

[C++ Core Guidelines] P.1, P.3 생각을 코드로 직접 나타내자

by 코드쉼터 2024. 3. 27.

P.1: Express ideas directly in code

P.3: Express intent

 

이유

컴파일러는 여러분이 쓴 주석을 전혀 읽지 않으며, 세상에 게으른 프로그래머들이 많기에 여러분이 쓴 주석에는 그다지 주의를 기울이지 않을 수 있습니다.

그럼 어떻게 해야할까요? 문법적으로 여러분의 의도를 코드로 명확히 표현해야 합니다 (주석 뿐만 아니라 코드 구조상)

 

 

예시 (나쁜 예)

class Date {
public:
    int month();          // 이렇게 쓰지 마세요
    // ...
};

위 코드에서 멤버 함수의 선언 int month(); 만 보면 리턴 값이 달(month) 을 나타내는 상수 함수인지 명확히 알 수 없습니다.

게으른 프로그래머들과 컴파일러는 멤버 함수의 선언 int month(); 만 보고는 month() 함수가 달력을 한달 뒤로 넘기고 성공하면 1을 반환하고 실패하면 -1을 반환하려는건지, 단순히 오늘이 몇 월인지를 반환하려는 함수인지 알 수가 없습니다. (심지어 이번 달에 휴일이 몇 개인지 반환하려는 것처럼 보이기도 하네요..)

 

 

예시 (좋은 예)

class Date {
public:
    Month month() const;  // 이렇게 쓰세요
    // ...
};

위는 좋은 방식으로 수정된 코드입니다. month() 멤버 함수가 반환하려는 값이 달(month) 이라는 사실이 명확히 드러나고 있으며, 상수 함수이므로 내부적으로 달력을 넘기지 않을 것이라는 것도 잘 드러나고 있습니다.

이렇게 생각을 코드로 명확히 표현하면 게으른 프로그래머들이 코드를 읽기도 편하고, 컴파일러가 여러분의 의도를 파악하여 실수를 막을 수도 있습니다.

 

 

예시 (나쁜 예)

void f(vector<string>& v)
{
    string val;
    cin >> val;
    // ...
    int index = -1;                    // 나쁜 코드, gsl::index 를 쓰는게 좋습니다
    for (int i = 0; i < v.size(); ++i) {
        if (v[i] == val) {
            index = i;
            break;
        }
    }
    // ...
}

 

음.. 위 코드를 볼까요? val 값을 하나 받고 그것으로 무언가(?) 하고 있습니다.

어디보자 어디보자.. 반복문을 돌면서 v 벡터에서 문자열을 하나씩 뽑아서 val 과 비교하고 있는 것처럼 보이네요..

index 는 뭐고 i 는 또 뭐고 아이고.. index 가 -1로 남아있으면 문자열을 못 찾은 걸로 생각하면 되는걸까요?

GSL(Guidelines Support Library) 을 사용해서 명확히 배열만을 인덱싱 하고자 표현 한다면 더욱 좋을 것 같은데 그것보다 반복문이 의도한 바가 명확히 드러나지 않는게 더 큰 문제입니다.

 

 

예시 (좋은 예)

void f(vector<string>& v)
{
    string val;
    cin >> val;
    // ...
    auto p = find(begin(v), end(v), val);  // better
    // ...
}

잘 디자인된 라이브러리(STL)를 사용하면 원시적인 언어 기능 (for, while) 들을 이용하는 것보다 프로그래머의 의도가 명확하게 드러나는 이쁜 코드를 작성할 수 있습니다.

 

 

예시 (나쁜 예)

change_speed(double s);   // s 는 대체 뭘 의미할까요?
// ...
change_speed(2.3);

위 double s 만 보고는 프로그래머가 어떤 타입을 표현하고 싶어했는지 모호합니다.

step, speed, seed(random seed), ... 여러가지 단어로 해석될 수도 있어 보이고,

speed 라고 하더라도 단위가 초속인지, 시속인지, 시속이면 km/h 인지, cm/h 인지 알 수 없습니다.

 

 

예시 (좋은 예)

change_speed(Speed s);    // s 가 의미하는 바가 명확합니다!
// ...
change_speed(2.3);        // 에러 : 단위가 누락됨
change_speed(23_m / 10s);  // 미터 / 초 로 속도를 계산하고 있군요

이제 s 는 속도를 나타내는 것이 명확해졌습니다.
C++11 User-defined literals 를 사용하면 속도의 단위도 명확하게 다루고 계산할 수 있습니다.

 

 

정리

const 를 일관되게 사용하세요. (멤버 함수가 개체를 수정하는지, 포인터나 참조로 전달된 인수를 내부적으로 수정하는지 나타내기)
묵시적 타입 변환에 의존하지 마세요. (직접 타입 정의하기)
가능하다면 잘 만들어진 표준 라이브러리를 사용하세요. (STL, GSL, Boost, ...)

 

주석에만 의존하지 마세요. 명쾌하게 프로그래머의 생각을 코드로 표현하는 방법은 많습니다.