본문 바로가기

C/C++

[Google C++ Style Guide] 7. Comments

Google C++ Style Guide

 

7. Comments (주석)

주석은 코드를 가독성 있게 유지시킨다다음의 규칙들은 무엇을 작성할지 어디에 작성할지를 기술한다주석은 매우 중요하지만최선의 코드는 self-documenting이라는 것을 기억해라타입과 변수 이름을 센스 있게 지으면 주석으로 설명하는 것보다 훨씬 낫다.

 

A.     Comment Style (주석 스타일)

// 또는 /* */ 를 사용하며일관성 있게 사용한다.

 

// 또는 /* */  사용하지만//이 더 많이 사용된다.

어떻게 사용하고 어떤 스타일로 사용하든지 일관성 있게 사용한다.

 

B.      File Comments (파일 주석)

각 파일의 시작은 저작권(copyright)을 게시하며파일 내용에 대한 설명을 기술한다.

 

Legal Notice and Author Line

모든 파일은 다음의 아이템을 순서대로 따른다.

-       저작권 문장 (copyright statement) (Copyright 2008 Google Inc)

-       라이선스 어구(license boilerplate) (Apache 2.0, BSD,LGPL, GPL)

-       파일의 원본 저자를 확인하기 위한 저자(author) 라인

 

누군가 작성한 파일에 중요한 변화를 작성했으면 저자 라인에 자신을 추가한다또 다른 기여자가 그 파일에 질문이 있거나연락할 필요가 있을 때 유용하다.

 

File Contents (파일 내용)

저작권과 저자에 대한 주석 다음에파일의 내용을 설명하는 주석을 작성한다.

 

일반적으로 .h파일은 파일에 선언된 클래스에 대한 내용을 기술할 것이다무엇을 위한 것이며 어떻게 사용되는지 등등. .cc파일은 구현에 대한 세부적인 정보나 알고리즘 트릭에 관한걸 작성한다이런 사항들을 .h파일에 작성하는 것이 유용하다면 .h파일에 작성하되, .cc파일에는  해당 주석이 .h파일에 있다고 언급한다.

 

.h파일과 .cc파일에 주석을 중복시키지 않는다.

 

C.      Class Comments (클래스 주석)

모든 클래스 정의는 무엇을 위한 것이며 어떻게 사용돼야 하는지 주석을 작성한다.

 

// GargantuanTable 내용을 탐색하는 Iterator,  사용 :

//    GargantuanTableIterator* iter = table->NewIterator();

//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {

//      process(iter->key(), iter->value());

//    }

//    delete iter;

class GargantuanTableIterator {

  ...

};

 

이미 파일의 상단에 작성이 되었다면, ‘자세한 설명은 파일의 상단에 있는 주석을 참고하라처럼 간단히 작성해도 좋다그러나 주석에 대한 분류는 해야 한다.

 

클래스에서 동기화에 대한 가정들을 문서화한다만약 클래스 인스턴스가 멀티 스레드에서 사용된다면 멀티스레드 환경에 대해 보다 주의 깊게 문서화 한다.

 

 

D.     Function Comments (함수 주석)

선언부에 함수의 사용을 설명한다정의부에서는 함수의 연산에 대해 설명한다.

 

Function Declarations (함수 선언부)

모든 함수 선언부는 함수가 어떤 일을 하고어떻게 사용하는지를 설명하는 주석을 즉시 작성한다이런 주석은 명령형(imperative)(“Open the file”) 보다는 설명적(“Opens the file”)이어야 한다주석은 함수를 설명하되함수가 무엇을 하는지는 설명하지 않는다일반적으로 이런 주석은 함수가 태스크를 어떻게 수행하는지 설명하지 않고이런 주석은 정의부에서 작성한다.

 

함수 선언부에서는 다음과 같은 것을 주석으로 작성한다.

-       input output이 무엇인지

-       멤버 함수에 대해 파라미터로 넘겨진 객체 참조자는 메소드 콜 후에도 유효한지객체가 free되는지.

-       함수를 호출한 곳에서 free 해야 하는 메모리를 할당 하는지.

-       어떤 파라미터가 NULL이 될 수 있는지.

-       함수가 사용되는 방법에 따라 성능에 영향을 줄 수 있는지

-       함수가 재진입(re-entrant) 되는지

 

예를 들면,

// Returns an iterator for this table.  It is the client's

// responsibility to delete the iterator when it is done with it,

// and it must not use the iterator once the GargantuanTable object

// on which the iterator was created has been deleted.

//

// The iterator is initially positioned at the beginning of the table.

//

// This method is equivalent to:

//    Iterator* iter = table->NewIterator();

//    iter->Seek("");

//    return iter;

// If you are going to immediately seek to another place in the

// returned iterator, it will be faster to use NewIterator()

// and avoid the extra seek.

IteratorGetIterator() const;

 

그러나불필요하게 장황하거나완전히 명백한 경우는 주석을 달지 않는다아래의 예는 그렇지 않으면 false를 리턴한다” 라는 말이 필요 없다.

 

// Returns true if the table cannot hold any more entries.

bool IsTableFull();

 

생성자와 소멸자에 주석을 달 때생성자와 소멸자가 무엇을 하는지 누구나 알기 때문에 객체를 파괴한다와 같은 주석은 달지 않는다생성자가 파라미터가 포인터인 경우에 포인터에 대한 소유권을 갖는지 등에 대해 주석을 작성한다중요하지 않은 주석은 작성하지 않는다.

 

Function Definitions (함수 정의부)

각 함수 정의부는 함수가 무엇을 하며어떻게 해당 job을 수행하는지 주석을 작성한다예를 들어정의부 주석은 코딩 트릭함수 구현 단계 개요변수를 사용하지 않고 왜 함수로 구현했는지 등을 설명한다혹은 왜 함수의 전반부에서 lock을 획득하고 후반부에서 그것이 필요하지 않은지 등을 설명할 수도 있다.

 

주의할 점은 함수 선언부의 주석과 중복하지 않는다함수가 하는 일을 간단히 언급해도 좋지만 주석의 목적은 어떻게 작성하느냐이다.

 

 

E.      Variable Comments (변수 주석)

일반적으로변수의 이름은 그 변수가 무엇을 위한 변수인지 충분히 설명할 수 있어야 한다어떤 경우에는 주석이 필요할 수도 있다.

 

Class Data Members (클래스 데이터 멤버)

각 클래스 멤버(인스턴스 변수 혹은 멤버 변수)는 무엇을 위해 사용되는 변수인지 설명하는 주석을 작성한다만약 변수가 특별한 의미로 감시 값(NULL이나 -1)을 갖는다면주석을 작성한다.

private:

 // Keeps track of the total number of entries in the table.

 // Used to ensure we do not go over the limit. -1 means

 // that we don't yet know how many entries the table has.

 int num_total_entries_;

             

Global Variables (전역 변수)

데이터 멤버처럼 모든 전역 변수는 그 변수가 무엇이고무엇을 위해 사용되는지를 설명하는 주석을 갖는다.

// The total number of tests cases that we run through in this regression test.

const int kNumTestCases = 6;

 

 

F.      Implementation Comments (구현부 주석)

구현부에서는 트릭적이고분명하지 않고관심을 끌고중요한 부분에 주석을 작성한다.

          

Class Data Members

트릭적이고 복잡한 코드는 주석이 필요하다.

// Divide result by two, taking into account that x

// contains the carry from the add.

for (int i = 0i < result->size(); i++) {

  x = (x << 8) + (*result)[i];

  (*result)[i] = x >> 1;

  x &= 1;

}

 

Line Comments

분명하지 않은 라인은 라인 끝에 주석을 작성한다라인 끝에 주석은 2 스페이스 공간을 준다.

// If we have enough memory, mmap the data portion too.

mmap_budget = max<int64>(0mmap_budget - index_->length());

if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytesmlock))

  return;  // Error already logged.

 

코드가 무엇을 하는지 설명하는 주석과 리턴할 때 에러는 이미 로그가 되었다는 주석 모두 있다는 것에 주목한다.

 

연속되는 라인에 주석을 작성하려면 아래와 같이 가독성 있게 작성한다.

DoSomething();       // Comment here so the comments line up.

DoSomethingElse();  // Comment here so there are two spaces between

                      // the code and the comment.

// One space before comment when opening a new scope is allowed,

  // thus the comment lines up with the following comments and code.

  DoSomethingElse();  // Two spaces before line comments normally.

}

 

NULL, true/false, 1, 2, 3 …

NULL, boolea, 문자 그대로의 정수값을 함수로 넘길 때그것이 무엇인지 주석을 작성하거나상수를 사용하여  self-document (자기 설명적)이 되도록 한다.

bool success = CalculateSomething(interesting_value,

                                      10,

                                      false,

                                      NULL);  //  파라미터는 뭘까??

                           

이렇게 작성한다.

bool success = CalculateSomething(interesting_value,

                                      10,     // 디폴트 base 

                                      false,  //  함수 호출이 처음이 아님

                                      NULL);  // No callback

 

또는 상수나 self-describe(자기 설명적)인 변수를 사용한다.

const int kDefaultBaseValue = 10;

const bool kFirstTimeCalling = false;

Callback *null_callback = NULL;

bool success = CalculateSomething(interesting_value,

                                      kDefaultBaseValue,

                                      kFirstTimeCalling,

                                      null_callback);

 

Don’ts (하지 말아야 할 것들)

절대 코드 자체를 설명하지 않는다.

// Now go through the b array and make sure that if i occurs,

// the next element is i+1.

...        // Geez.   의미 없는 주석은 뭔가.

 

 

G.     Punctuation, Spelling and Grammar (구두점스펠링문법)

구두점(쉼표콜론, ~ ), 스펠링 문법에 주의를 기울인다나쁘게 작성하는 것보다 잘 작성하는 것이 읽기 쉽다.

 

주석은 보통 적당한 대문자와 적당한 길이를 갖는 완벽한 문장으로 작성한다라인 끝에 작성하는 주석과 같은 짧은 주석은 때로 덜 비공식적이지만스타일에 맞게 일관성 있게 사용하면 된다완벽한 문장은 더 읽기 쉽고주석을 더 완전하고미완된 생각이 아니라는 것을 보장해 준다.

 

세미콜론을 사용할 때코드 리뷰어가 콤마를 사용하라고 지적하면 짜증나겠지만소스 코드를 명료하고 가독성 있는 하이 레벨로 유지시키는 것이 중요하다적당한 구두점스펠링문법은 이런 목적에 도움을 준다.

 

 

H.     TODO Comments (해야 할 일에 대한 주석)

일시적이고단기 솔루션완벽하지 않지만 충분한 코드의 경우 TODO 주석을 사용한다.

 

TODO는 TODO 문자열을 포함하며이름과 이메일 주소를 쓰거나괄호 내에서 다른 지시자를 쓴다콜론은 선택사항이다주 목적은 주석을 작성하는 사람에 의해서 검색이 가능하도록 일관성있도록 작성한다. TODO는 자신의 코드를 수정하기 위해서 주석을 작성하지는 않는다.

 

// TODO(kl@gmail.com): Use a "*" here for concatenation operator.

// TODO(Zeke) change this to use relations.

 

TODO가 미래 날짜에 할 일의 형태라면 특정 날짜(“Fix by November 2005”) 또는 특정 이벤트(“모든 클라이언트가XML 응답을 처리할 수 있을 때 제거한다”)를 확실히 해둔다.

 

 

I.       Deprecation Comments (비추천주석)

Deprecated 인터페이스 포인트에 DEPRECATED 주석으로 표시한다.

 

인터페이스에 단어 DEPRECATED를 포함하는 주석을 작성하여 비추천(deprecated)할 수 있다주석은 인터페이스 선언부 앞에 하거나 선언부와 같은 라인에 작성한다.

 

DEPRECATED 단어 이후에 이름과 주소를 쓰거나괄호 내에 다른 지시자를 쓴다.

 

Deprecation 주석은 호출한 곳(callsite)을 수정하도록 간단하고 명료하게 작성한다. C++에서는 새로운 인터페이스 포인트를 호출하는 인라인 함수로서 deprecated 함수를 구현할 수 있다.

 

인터페이스 포인트를 DEPRECATED로 표시한다고 해서 모든 callsite 코드가 변경되지는 않는다만약 실제로 deprecated 함수를 사용하지 못하게 하려면, callsite를 직접 수정해야 한다.

 

새로운 코드는 deprecated 인터페이스 포인트를 호출하지 않아야 하며대신 새로운 인터페이스 포인트를 사용한다.