C++

[C++]CppCoreGuideLine(2) - Philosophy

얌몽 2019. 1. 26. 10:36

- 원문 출처 : https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#S-philosophy

-------------------------------------------------------------------------------------------------------------------------------------

개요

다른 가이드라인을 정하기 전에 일반적인 규칙부터 정의한다. 철학적으로 제시되는 규칙들은 기계적으로 체크할 수는 없다. 하지만 이런 철학적은 주제를 반영하는 규칙들은 기계적으로 체크할 수 있다. 철학적인 근거를 납득할 수 없으면 이 후에 제시되는 구체적인 규칙들의 근거가 부족할 것이다.

P.1 : 생각을 코드로 직접 표현해라

컴파일러가 주석이나 문서를 읽지 않는것 처럼 많은 프로그래머들도 그렇다. 코드로 표현된다는 것은 의미를 정의한다는 것이고, 다른 컴파일러나 도구 또한 알아야 한다.

P.2 : C++ ISO 표준을 사용해라

P.3 : 표현식에 의도를 표현하자

어떤 코드의 의도가 네이밍이나 주석으로 서술되어 있지 않으면 이 코드의 목적이 뭔지 알 수 없다. 표현식으로 이 코드의 의도가 무엇인지 알수 있게 해야 한다.

예시

1
2
3
4
gsl::index i = 0;
while (i < v.size()) {
    // ... do something with v[i] ...
}
cs

위의 예시에서는 단순히 v의 요소들을 루프 도는 의도가 표현되어 있지 않다. index 구성 변수 i가 loop 밖에 선언되어 있고, 의도가 불분명한다. 코드를 읽는 사람은 이 부분만 봐서는 의도를 파악하기 힘들다.

개선

1
for (const auto& x : v) { /* do something with the value of x */ }
cs

위의 코드에서는 루프를 돌기위한 외부 작업이 없고, const refernece를 보니, 루프 작업시 수정 작업이 없다는 것을 확인할 수 있다. 만약 수정 작업이 요구되면 아래와 같이 표현하면 된다

1
for (auto& x : v) { /* do something with the value of x */ }
cs

P.4 : 프로그램은 Type-Safe 해야한다.

P.5 : 런타임 체크보다는 컴파일 타임 체크를 선호하자.

컴파일 타임에 에러가 잡히면 에러 핸들링을 할 필요가 없다. 이 경우 코드가 명확해지고 퍼포먼스가 증가한다.

P.6 : 컴파일 타임에 체크되지 않는건 런타임에 체크될 수 있어야 한다.

프로그램에서 에러를 찾기 힘들게 하면 크래시나 원치 않은 결과가 나타난다. 이상적으로는 컴파일타임이나 런타임에 모든 에러를 체크하고 처리할 수 있다. 하지만 모든 에러를 컴파일 타임에 처리하는 것은 불가능하고, 런타임 처리도 모두 잡을수는 없을 것이다. 하지만 충분한 자원을 가지고 원칙적으로 점검할 수 있는 프로그램을 작성해야 한다.

나쁜 예시

1
2
3
4
5
6
7
8
// separately compiled, possibly dynamically loaded
extern void f(int* p);
 
void g(int n)
{
    // bad: the number of elements is not passed to f()
    f(new int[n]);
}
cs

p.7 : 런타임 에러는 최대한 일찍 체크하자.

P.8 : 메모리 누수는 어떤 리소스던 발생시키면 안된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Bad Exapmle
void f(char* name)
{
    FILE* input = fopen(name, "r");
    // ...
    if (something) return;   // bad: if something == true, a file handle is leaked
    // ...
    fclose(input);
}
 
===========================================================================================
 
// Prefer RAII
void f(char* name)
{
    ifstream input {name};
    // ...
    if (something) return;   // OK: no leak
    // ...
}
cs

P.9 : 시간이나 공간을 낭비하지 마라

지금 쓰는건 C++다. (역주 : 간지난다. C++ 개발자들이 다른 언어들에 비해 느끼는 자부심이 돋보인다.)

나쁜 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct X {
    char ch;
    int i;
    string s;
    char ch2;
 
    X& operator=(const X& a);
    X(const X&);
};
 
X waste(const char* p)
{
    if (!p) throw Nullptr_error{};
    int n = strlen(p);
    auto buf = new char[n];
    if (!buf) throw Allocation_error{};
    for (int i = 0; i < n; ++i) buf[i] = p[i];
    // ... manipulate buffer ...
    X x;
    x.ch = 'a';
    x.s = string(n);    // give x.s space for *p
    for (gsl::index i = 0; i < x.s.size(); ++i) x.s[i] = buf[i];  // copy buf into x.s
    delete[] buf;
    return x;
}
 
void driver()
{
    X x = waste("Typical argument");
    // ...
}
cs
위의 예시에서, x는 최소 6바이트를 낭비한다. Buf 함수에 대한 new & delete 도 필요 없다. 만약 필요하다면 로컬 string 함수를 이용하면 된다.

P.10 : 변경 불가능한 변수를 선호해라.

변수와 상수 관계를 생각해보면, 상수는 기대되지 않게 변경시킬 수 없다. 상수를 사용하면 내가 원하는 결과를 도출할 수 있다. 

P.11 : 코드를 파편화 시키지 말고, 지저분한 구조를 캡슐화 해라.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Bad Example
int sz = 100;
int* p = (int*malloc(sizeof(int* sz);
int count = 0;
// ...
for (;;) {
    // ... read an int into x, exit loop if end of file is reached ...
    // ... check that x is valid ...
    if (count == sz)
        p = (int*realloc(p, sizeof(int* sz * 2);
    p[count++= x;
    // ...
}
 
================================================================
// Better. Use vector
vector<int> v;
v.reserve(100);
// ...
for (int x; cin >> x; ) {
    // ... check that x is valid ...
    v.push_back(x);
}
cs

P.12 : 적절한 지원 도구를 사용해라

P.13 : 필요에 따라 적절한 라이브러리를 사용해라.

잘 디자인, 문서화 되어 있는 라이브러리를 사용하면 시간과 노력을 절약할수 있다. 보통 문서화하는 시간이 코드를 작성하는 시간 만큼 많은 시간을 필요로 한다.

예시

1
std::sort(begin(v), end(v), std::greater<>());
cs

정렬 알고리즘의 전문가가 아니거나 엄청난 시간을 정렬 시간에 투자하지 않았으면 대부분 위의 함수가 정확하고 꽤 빠른 답을 찾아줄 것이다. 표준 라이브러리를 써야하는 이유보다는 안써야 되는 이유를 찾아야 할것이다.