Skip to content

Latest commit

 

History

History
576 lines (447 loc) · 27.8 KB

File metadata and controls

576 lines (447 loc) · 27.8 KB

C++ 코딩 표준

본 코딩 표준은 작성자가 c++코드 작성에 사용할 표준입니다.
구글 코딩 스타일 가이드, 언리얼 코딩 표준, POCU 아카데미 코딩 표준을 참고하여 작성하고 있습니다.(진행중)

기본 원칙

  1. 가독성
  2. 문제가 있을 경우 가능한 한 빠르게 크래시가 나거나 assert에 걸리도록 코드를 작성
  3. 가능한 한 통합개발환경의 자동서식을 준수(자동완성 기능)

차례

  1. 헤더파일
  2. 범위
  3. 클래스
  4. 그 외의 기능
  5. 이름 규칙
  6. 주석
  7. 포매팅
  8. 규칙의 예외사항

헤더파일

#define

  • 가드 모든 헤더 파일은 여러 번 포함되지 않기 위해 #define 가드를 사용해야 한다. 기호의 포맷은 _<PROJECT>___<PATH>___<FILE>__H_ 으로 한다.

전방선언

  • 불필요한 \#include를 피하기 위해 클래스를 전방 선언할 수도 있다.
  • 클래스를 상호 참조할 때는 \#include보다 전방선언을 최대한 이용한다.

인라인 함수

  • 함수가 작을 때, 이를테면 10라인이나 그 이하일 정도일 때만 함수를 인라인으로 정의하라

함수 인자 순서

  • 함수를 정의할 때 인자 순서는 입력이 먼저이고 출력이 다음이다.

include의 이름과 순서

  • include 전처리문 블록과 코드 본문 사이에 반드시 빈 줄이 있어야 한다.
  • 가독성을 높이고 숨겨진 종속성을 피하기 위해서 다음과 같은 순서를 사용하라.: C 라이브러리, C++라이브러리, 다른 라이브러리들의 .h, 현재 프로젝트의 .h
  • 순서를 적용할 때 가능하다면 알파벳 순서를 따른다.

include <>, include ""

  • 인클루드 라이브러리를 \#include할 때 표준, 외부 헤더 파일을 인클루드 할 때는 <>, 자체적으로 만든 헤더 파일은 " " 을 사용한다.

범위

네임스페이스

  • .cc 파일에 이름 없는 네임스페이스 사용을 권장한다. 이름 있는 네임스페이스는 프로젝트에 기반한, 주로 경로에 기반한 이름을 선택하라. _using 지시자_를 사용하지 말라. 인라인 네임스페이스를 사용하지 말라.

중첩 클래스

  • API의 일부인 경우엔 public 중첩 클래스를 사용할 수 있다. 하지만 전체 영역(전역)에 선언이 들어가지 않도록 네임스페이스의 사용을 고려하라.

const

  • 원칙적으로 모든 곳에 const를 사용한다. 여기에는 지역 변수와 함수 매개 변수도 포함된다.
  • 개체를 수정하지 않는 멤버 함수에는 모두 const를 붙인다
    • 예: int GetAge() const;

매개 변수

  • 함수에서 매개변수를 통해 값을 반환할 때(out 매개변수)는 포인터를 사용하며, 매개변수 이름 앞에 out을 붙인다.

    함수:

    void GetScreenDimension(uint32_t* const outWidth, uint32_t* const  outHeight)
    {
    }

    호출:

    uint32_t width;
    uint32_t height;
    GetScreenDimension(&width, &height);
  • 위 항목의 out 매개 변수는 반드시 null이 아니어야 한다. (함수 내부에서 if 문 대신 assert를 사용할 것)

    void GetScreenDimension(uint32_t* const outWidth, uint32_t* const  outHeight)
    {
        Assert(outWidth);
        Assert(outHeight);
    }
  • 매개 변수가 클래스 내부에서 저장될 때는 포인터를 사용한다.

    void AddMesh(Mesh* const mesh)
    {
        mMeshCollection.push_back(mesh);
    }
  • 매개 변수가 void 포인터여야 하는 경우는 포인터를 사용한다.

    void Update(void* const something)
    {
    }
  • 디폴트 매개 변수 대신 함수 오버로딩을 선호한다.

  • 디폴트 매개 변수를 사용하는 경우, nullptrfalse, 0 같이 비트 패턴이 0인 값을 사용한다.

지역 변수

  • 함수의 변수는 가능한 한 좁은 범위에 두고, 선언에서 초기화하라.
  • 지역 변수를 선언할 때는 그 지역 변수를 사용하는 코드와 동일한 줄에 선언하는 것을 원칙으로 한다.

정적 변수와 전역 변수

  • 클래스 타입의 정적 변수와 전역 변수는 금지한다. 그것들은 특정할 수 없는 생성과 소멸 순서로 인해 찾기 힘든 버그들을 만든다. 하지만 이들 변수가 constexpr인 경우는 허용한다. 이들은 동적으로 초기화되거나 소멸하지 않는다.

전역 변수, 정적 변수, 정적 클래스 멤버 변수, 함수 정적 변수 등의 정적 저장 기간을 가진 객체들은 반드시 Plain Old Data (POD)여야 한다. 오직 int, char, float, 포인터, 혹은 POD의 배열, POD의 구조체여야 한다.

정적 변수의 클래스 생성자와 초기화 함수가 호출되는 순서는 C++에서 부분적으로만 특정되어 있고, 심지어 각각의 빌드마다 호출 순서가 변경될 수 있어 찾기 힘든 버그를 만들 수 있다. 그러므로 우리는 클래스 타입의 전역 변수를 금지하며, 또한 (getenv()getpid()처럼) 다른 전역 요소에 전적으로 의존하는 함수가 아닌 한 함수를 사용하여 정적 POD 변수를 초기화할 수 없다.

이와 비슷하게 전역 변수와 정적 변수는 main() 함수에서 리턴하거나 exit()의 호출 여부에 무관하게 프로그램이 종료될 때 소멸한다. 소멸자의 호출 순서는 생성자의 호출 순서의 반대로 정의된다. 생성자의 호출 순서를 특정할 수 없기 때문에 소멸자의 호출 순서도 마찬가지이다. 예를 들어 프로그램이 종료되는 시점에서 정적 변수는 이미 소멸되었을 수 있고, 하지만 여전히 수행 중인 코드가 (아마도 다른 스레드에 있는) 그것에 접근하려고 시도하고 실패할 수 있다. 혹은 어느 정적 string 변수의 소멸자가 그 문자열을 참조하는 다른 변수에 대한 소멸자보다 먼저 수행될 수도 있다.

소멸자 문제를 완화하기 위해 exit() 대신 quick_exit()을 불러 프로그램을 종료할 수도 있다. 둘의 차이점은 quick_exit()는 소멸자를 호출하지 않으며 atexit()를 호출하여 등록된 핸들러들을 호출하지 않는 것이다. 만약 quick_exit()를 사용해서 프로그램을 종료할 때 수행해야 하는 핸들러가 있는 경우 (예를 들면 로그를 flush 한다거나), at_quick_exit()를 사용하여 등록할 수 있다. (만약 exit()quick_exit()에 모두 수행해야 하는 핸들러가 있다면 두 곳 모두 등록해야 한다.)

결과적으로 우리는 POD 데이터로 이루어진 가지는 정적 변수만을 허용한다. 이 규칙은 vector(대신 C 배열을 사용하라)나 string(대신 const char []를 사용하라)을 완전히 금지한다.

만약 클래스 타입의 정적 변수나 전역 변수가 필요하면 main() 함수나 pthread_once()에서 (절대로 해제되지 않는) 포인터를 초기화하는 방법을 고려하라. 이것은 "스마트"한 포인터가 아닌 그냥 포인터여야 하는 데 주의하라. 스마트 포인터의 소멸자는 우리가 피하려 하고 있는 소멸자 순서 문제를 야기할 것이기 때문이다.

구조체

멤버 변수

  • 구조체는 오직 public 멤버 변수만 가질 수 있다. 구조체의 멤버 변수명은 파스칼 표기법을 따르며, 구조체 안에서 함수는 사용하지 않는다.
struct MeshData
{
	int32_t	VertexCount;
}

클래스

클래스는 C++ 코드에서 널리 사용하는 기본적인 단위이다. 이 섹션은 클래스를 작성할 때 지켜야 할 것과 하지 말아야 할 것을 나열한다

생성자에서의 작업

  • 생성자에서 복잡한 초기화 작업을 하는 것을 피하라 (특히 실패할 수 있는 초기화나 가상 메서드 호출이 필요한 초기화).

초기화

  • 클래스의 멤버 변수를 정의할 때 반드시 모든 멤버 변수에 대해 클래스 내 초기화 구문을 제공하거나 생성자를 작성해야 한다 (이는 디폴트 생성자일 수 있다). 생성자를 직접 선언하지 않은 경우 컴파일러는 일부 필드를 초기화되지 않은 채로 내버려 두거나 부적절한 값으로 초기화시키는 디폴트 생성자를 만들 것이다.
  • 초기화 리스트를 이용해 멤버 변수를 초기화할 때는 아래와 같은 포맷을 따라 한 줄에 변수 하나씩 초기화한다. 틀린 방식:
MyClass::MyClass(const int var1, const int var2)
	:mVar1(var1), mVar2(var2), mVar3(0)
{

올바른 방식:

MyClass::MyClass(const int var1, const int var2)
	: mVar1(var1)
	, mVar2(var2)
	, mVar3(0)
{		

explicit 생성자

  • 인자가 하나인 생성자에는 C++ 키워드인 explicit을 사용하라.

복사 생성자

  • 필요한 경우 복사 생성자와 대입 연산자를 작성하라. 그 외에는 DISALLOW_COPY_AND_ASSIGN로 그들을 쓸 수 없게 하라.

생성자 위임과 생성자 상속

  • 코드 중복을 줄일 수 있다면 생성자 위임과 상속을 사용하라.

구조체 대 클래스

  • 데이터를 나르는 수동적인 객체의 경우에만 struct를 사용하라. 그 외의 모든 경우에는 class를 쓰라.

상속

  • 컴포지션이 종종 상속보다 더 적절하다. 상속을 쓰려면 public으로 하라.

다중 상속

  • 다중 구현 상속이 실제로 유용한 경우는 매우 드물다. 우리는 상위 클래스들 중 최대 하나가 구현을 가진 경우에만 다중 상속을 허용한다. 다른 모든 상위 클래스는 Interface 접미어가 달린 순수한 인터페이스 클래스여야 한다.

인터페이스

  • 특정 조건을 만족하는 클래스만 Interface라는 접미어로 끝날 수 있다.

연산자 오버로드

  • 흔치 않은 특별한 상황이 아니면 연산자를 오버로드하지 말라. 사용자가 정의한 리터럴을 만들지 말라.
  • const 반환을 위한 함수 오버로딩은 허용한다.
Anim* GetAnimByIndex(const int index);
const Anim* GetAnimByIndex(const int index) const;

접근 제어

  • 데이터 멤버를 private으로 만들고, 필요한 경우 접근자 함수를 통해 접근을 제공하라 (기술적인 이유로 우리는 Google Test를 사용할 때 test fixture 클래스의 데이터 멤버가 protected인 것을 허용한다). foo_라고 불리는 변수가 있다면 접근자 함수는 전형적으로 foo()라고 불린다. 또한 set_foo()라는 변경자 함수가 필요할 수도 있다. 예외: static const 데이터 멤버(전형적으로 kFoo라고 불리는)들은 private여야 할 필요가 없다.
  • 클래스 멤버변수에 접근할 때는 항상 setter와 getter를 사용한다.

선언 순서

  1. friend 클래스들
  2. public 메서드들
  3. protected 메서드들
  4. privaate 메서드들
  5. protected 변수들
  6. private 변수들

짧은 함수를 쓰기

  • 짧고 집중된 함수를 선호하라.
  • main, routine은 최대 60줄 그 외 함수는 최대 25줄 이내로 작성한다.

소스 파일

  • 클래스는 각각 독립된 소스 파일에 있어야 한다.

구글만의 특별한 마법

다른 곳에서 본 것과 다를 수 있는, 구글에서 C++ 코드를 더 튼튼하게 만들기 위해 사용하는 다양한 기법과 장치들이 있다.

소유권과 스마트 포인터

  • 동적으로 할당된 객체들에는 하나의 고정된 소유자를 두는 것을 선호하라. 스마트 포인터를 써서 소유권을 이전하는 것을 선호하라.

cpplint

  • 스타일 오류를 찾기 위해 cpplint.py를 사용하라.

이름 규칙

일관성을 위한 가장 중요한 규칙은 이름 규칙을 통제하는 것이다. 이름의 스타일을 통해 요소의 선언을 찾지 않고도 해당 요소가 타입인지, 변수인지, 함수인지, 상수인지, 혹은 매크로인지 바로 알 수 있다. 우리 머리 속의 패턴매칭 엔진은 이러한 이름 규칙에 상당히 의존한다.

이름 규칙은 상당히 모호하지만 이 영역에서 개인의 선호도보다 일관성이 더 중요하다고 본다. 합리적이라고 생각하든 아니든 규칙은 지켜져야 한다.

일반 이름 규칙

  • 함수 이름, 변수 이름, 파일 이름은 약어를 피하고 서술적으로 지어야 한다.

파일 이름

  • 파일 이름은 모두 소문자이어야 하고 언더스코어 (_) 혹은 대쉬 (-)를 포함할 수 있다. 반드시 언더스코어를 사용할 필요는 없고 프로젝트에서 사용하는 관례를 따른다.

타입 이름

  • 타입 이름은 대문자로 시작하며 언더스코어 없이 단어마다 첫 글자로 대문자를 사용한다. 예를 들면 MyExcitingClass, MyExcitingEnum.

변수 이름

  • 변수 이름은 모두 소문자로 작성하며 단어 사이에 언더스코어를 사용한다. 클래스 멤버 변수는 이름의 끝에 언더스코어를 사용한다. 예를 들면 my_exciting_local_variable, my_exciting_member_variable_.

상수 이름

  • k로 시작하는 대소문자가 섞인 이름을 사용한다. 예를 들면 kDaysInAWeek.
  • 상수는 #define 보다는 const 상수 변수로 선언한다.
  • #define 으로 정의된 상수는 항상 괄호로 감싸준다.

함수 이름

  • 일반 함수들은 대소문자가 섞인 방식을 사용한다. 접근자와 변경자는 해당하는 변수의 이름과 같은 것을 쓴다. MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().

네임스페이스 이름

  • 네임스페이스 이름은 모두 소문자로 하며, 프로젝트의 이름과 아마도 디렉터리 구조에 기반하여 작성한다. 예를 들면 google_awesome_project.

열거형 이름

  • 열거형은 상수 또는 매크로 방식 중 하나로 작성한다. kEnumName 또는 ENUM_NAME.

  • 비트 플래그 열거형은 이름 뒤에 Flags를 붙인다.

    enum class eVisibilityFlags
    {
    }
  • 특정 크기(예를 들어, 데이터 멤버의 직렬화를 위한 크기)가 필요하지 않은 한 열거형에 크기 지정자를 추가하지 않는다.

    enum class eDirection : uint8_t
    {
        North,
        South
    }

매크로 이름

  • 정말로 매크로를 정의하려고 하는가? 그렇다면 다음과 같이 하라. MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

이름 규칙의 예외

  • 이미 존재하는 C 또는 C++ 엔티티와 비슷한 것의 이름을 짓는 경우 그 이름 규칙을 따를 수 있다.
  1. 클래스와 구조체의 이름은 파스칼 표기법을 따른다.(고저고저)

  2. 지역 변수 그리고 함수의 매개 변수의 이름은 카멜 표기법을 따른다.(저고저)

  3. 메서드 또는 함수 이름은 동사로 시작한다.

    • a. public 메서드의 이름은 파스칼 표기법을 따른다.(고저고저)
    • b. 그 외 다른 메서드의 이름은 파스칼 카멜 표기법을 따른다.(저고저)
  4. 상수 또는 #define 으로 정의된 상수의 이름은 모두 대문자로 하되 밑줄로 간 단어를 분리한다.

const int SOME_CONSTANT = 1;

주석

작성하는 것이 쉽지 않지만, 주석은 코드의 가독성을 유지하는 데 절대적으로 중요한 역할을 한다. 다음 규칙은 어디에 무엇을 주석으로 달아야 할 지를 설명한다. 하지만 기억할 점은 주석은 매우 중요하지만, 가장 좋은 코드는 스스로에 대해 설명을 할 수 있는 코드라는 것이다. 타입과 변수에 이해할 수 있는 이름을 짓는 것이 이상한 이름을 짓고 주석으로 설명하는 것보다 훨씬 좋다.

주석을 작성할 때에는 주석을 읽는 이를 위해서, 즉 그 코드를 보고 이해해야 하는 다음 작업자를 위해 작성하라. 관대함을 가지라 - 다음 작업자는 본인일 수도 있다!

주석 스타일

  • 일관성 있게 // 또는 /* */ 문법을 사용한다.

파일 주석

  • 모든 파일의 시작 부분에는 라이선스 문안이 있어야 하고, 그 뒤에 내용에 대한 설명이 있어야 한다.

클래스 주석

  • 모든 클래스 정의는 그것이 무엇이고 어떻게 사용하는 지 설명하는, 클래스에 수반하는 주석을 가져야 한다.

함수 주석

  • 선언 주석은 함수의 사용을 설명한다. 함수 정의의 주석은 함수의 동작을 설명한다.

변수 주석

  • 일반적으로 변수의 실제 이름은 그 변수가 무엇을 위한 것인지 알아채기 쉽도록 설명적이어야 한다. 어떤 경우에는 더 많은 주석이 필요하다.

구현 주석

  • 구현에서 까다롭거나, 명백하지 않거나, 흥미롭거나, 중요한 부분은 주석을 가져야 한다.

구두점, 철자, 문법

  • 구두점, 철자, 문법에 주의하라. 구두점, 철자, 문법이 제대로 사용된 주석이 이해하기 쉽다.

TODO 주석

  • 아직 완벽하지 않은 코드 혹은 임시적이고 단기적인 해결책에 TODO 주석을 사용하라.

Deprecation 주석

  • Deprecate된 인터페이스는 DEPRECATED 주석으로 표시해 둔다.

포매팅

코딩 스타일과 포매팅은 제멋대로인 경우가 많지만, 모두가 통일된 스타일을 쓴다면 프로젝트를 파악하기가 훨씬 쉬워진다. 개개인이 모든 포매팅 규칙에 다 동의하기는 어렵고, 어떤 규칙은 익숙해지는데 시간이 걸리지만, 프로젝트의 구성원들이 규칙을 따라서 다른 사람의 코드를 쉽게 이해하도록 하는 것은 중요하다.

코드를 형식에 맞게 작성하는 것을 돕기 위해 emacs 설정파일 을 만들었다.

줄 길이

  • 코드의 각 줄은 80 문자를 넘지 않게 하라.

ASCII가 아닌 문자

  • ASCII가 아닌 문자는 자주 사용하지 않아야 하며, 반드시 UTF-8 포매팅을 사용해야 한다.

스페이스 대 탭

  • 스페이스만 사용하고 2개의 스페이스로 들여쓰기 하라.

함수 선언과 정의

  • 가능하면 리턴 타입과 함수 이름, 인자를 같은 줄에 작성하라.

함수 호출

  • 자리가 충분한 경우 한 줄에 쓰고, 그렇지 않은 경우 괄호 안의 인자들을 줄바꿈한다.

중괄호 초기화 리스트

  • 중괄호로 된 초기화 리스트는 함수 호출에서와 완전히 같은 방식으로 작성한다.

조건문

  • 괄호 안에서는 스페이스를 사용하지 않을 것을 권장한다. else 키워드는 새 줄에서 사용한다.

반복문과 switch 문

  • switch 문은 블록마다 중괄호를 사용할 수 있다. 단순하지 않게 이어지는 case 문들은 주석을 달아야 한다. 비어 있는 반복문은 {} 또는 continue를 사용해야 한다.
  • 항상 default를 넣는다.
switch (number)
{
case 0:
	...
	break;
default:
	break;
}
  • default case가 절대 실행될 일이 없는 경우, default case 안에 Assert(false); 란 코드를 추가한다. Assert()를 직접 구현하면 그 안에서 릴리즈 빌드 시 최적화 힌트를 추가할 수 있다.

포인터와 레퍼런스 표현식

  • 마침표나 화살표 좌우에는 스페이스를 사용하지 않는다. 포인터 연산자 뒤에는 스페이스를 사용하지 않는다.
  • 포인터나 참조 기호는 자료형에 붙인다.
int& number;
int* number;

불리언 표현식

  • 정해진 줄 길이보다 긴 불리언 표현식을 사용해야 할 때에는 줄바꿈을 일관성있게 해야 한다.

리턴값

  • return 표현식을 불필요하게 괄호로 묶지 않아야 한다.

변수와 배열 초기화

  • =, (), {} 중에서 선택할 수 있다.

전처리기 지시자

  • 전처리기 지시자 시작 부분에 사용되는 # 기호는 항상 그 줄의 처음에 있어야 한다.

클래스 포맷

  • public, protected, private 순서로 각각 스페이스 하나씩 들여쓰기하여 클래스 본문을 구성한다.

생성자 초기화 리스트

  • 생성자 초기화 리스트는 모두 한 줄에 쓰거나, 스페이스 4개로 들여쓰기하여 각각 다음 줄에 작성할 수 있다.

네임스페이스 포매팅

  • 네임스페이스의 내부는 들여쓰기하지 않는다.

가로 공백

  • 가로 공백을 사용하는 것은 경우에 따라 다르다. 줄 끝에는 공백 문자를 사용하지 않는다.

세로 공백

  • 세로 공백의 사용을 최소화하라
  1. include 전처리문 블록과 코드 본문 사이에 반드시 빈 줄이 있어야 한다.

  2. 탭(tab)은 비주얼 스튜디오 기본값을 사용하며, 비주얼 스튜디오를 사용하지 않을 시 띄어쓰기 4칸을 탭으로 사용한다.

  3. 중괄호( { )를 열 때는 언제나 새로운 줄에 연다.

  4. 중괄호 안( { } )에 코드가 한 줄만 있더라도 반드시 중괄호를 사용한다.

     if (bSomething)
     {
         return;
     }
  5. 포인터나 참조 기호는 자료형에 붙인다.

     int& number;
     int* number;
  6. 초기화 리스트를 이용해 멤버 변수를 초기화할 때는 아래와 같은 포맷을 따라 한 줄에 변수 하나씩 초기화한다. 틀린 방식:

     MyClass::MyClass(const int var1, const int var2)
       :mVar1(var1), mVar2(var2), mVar3(0)
     {

    올바른 방식:

     MyClass::MyClass(const int var1, const int var2)
       : mVar1(var1)
       , mVar2(var2)
       , mVar3(0)
     {

이름 규칙

일관성을 위한 가장 중요한 규칙은 이름 규칙을 통제하는 것이다. 이름의 스타일을 통해 요소의 선언을 찾지 않고도 해당 요소가 타입인지, 변수인지, 함수인지, 상수인지, 혹은 매크로인지 바로 알 수 있다. 우리 머리 속의 패턴매칭 엔진은 이러한 이름 규칙에 상당히 의존한다.

이름 규칙은 상당히 모호하지만 이 영역에서 개인의 선호도보다 일관성이 더 중요하다고 본다. 합리적이라고 생각하든 아니든 규칙은 지켜져야 한다.

일반 이름 규칙

  • 함수 이름, 변수 이름, 파일 이름은 약어를 피하고 서술적으로 지어야 한다.

파일 이름

  • 파일 이름은 대소문자까지 포함해서 반드시 클래스 이름과 일치해야 한다.
class PlayerAnimation;

PlayerAnimation.cpp
PlayerAnimation.h
  • 여러 파일이 하나의 클래스를 이룰 때, 파일 이름은 클래스 이름으로 시작하고, 그 뒤에 밑줄과 세부 항목 이름을 붙인다.
class RenderWorld;

RenderWorld_load.cpp
RenderWorld_demo.cpp
RenderWorld_portals.cpp
  • Reverse OOP 패턴을 사용할 때, 플랫폼 전용 클래스는 위 항목과 비슷한 명명 규칙을 사용한다.

    class Renderer;
    
    Renderer.h			// 게임에서 호출되는 모든 Renderer 인터페이스
    Renderer.cpp		// 모든 플랫폼 용 Renderer 구현 소스
    Renderer_gl.h		// Renderer가 호출하는 RendererGL 인터페이스
    Renderer_gl.cpp		// RendererGL 구현 소스

타입 이름

  • 타입 이름은 대문자로 시작하며 언더스코어 없이 단어마다 첫 글자로 대문자를 사용한다. 예를 들면 MyExcitingClass, MyExcitingEnum.

변수 이름

  • 지역 변수 그리고 함수의 매개 변수의 이름은 카멜 표기법을 따른다.
void SomeMethod(const int someParameter)
{
	int someNumber;
	int	id;
}
  • 클래스 멤버 변수 변수면은 앞에 m을 붙인가.
class Employee
{
protected:
	int mDepartmentID;
private:
	int mAge;
}
  • 부울(boolean)형 변수는 앞에 b를 붙인다.

상수 이름

  • k로 시작하는 대소문자가 섞인 이름을 사용한다. 예를 들면 kDaysInAWeek.

클래스 이름, 구조체이름

  • 클래스와 구조체의 이름은 파스칼 표기법을 따른다.
class PlayManager;
struct AnimationInfo;
  • 인터페이스를 선언할 때는 앞에 I 를 붙인다.
    • class ISomeInterface;

함수 이름

  • 메서드 또는 함수 이름은 동사로 시작한다.
    • public 메서드의 이름은 파스칼 표기법을 따른다.
    • 그 외 다른 메서드의 이름은 카멜 표기법을 따른다.
  • 접근자와 변경자는 해당하는 변수의 이름과 같은 것을 쓴다.
    • 예: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().
  • 값을 반환하는 함수의 이름은 무엇을 반환하는지 알 수 있게 짓는다.
    • 예: uint32_t GetAge() const;
  • 값(value)형식의 변수를 const로 반환하지 않는다. 포인터나 참조(reference)를 반환할 경우에만 const 반환을 한다.
  • 재귀 함수는 이름 뒤에 Recursive 를 붙인다.

네임스페이스 이름

  • 네임스페이스 이름은 모두 소문자로 작성한다.
  • 프로젝트의 이름과 아마도 디렉터리 구조에 기반하여 작성한다.
    • 예: google_awesome_project.

열거형 이름

  • 열거형은 상수 또는 매크로 방식 중 하나로 작성한다. kEnumName 또는 ENUM_NAME.
  • 열거형을 선언할 때는 앞에 e를 붙인다.
enum class eDirection
{
	North,
	South
}

라벨 이름

  • goto 레이블 명은 모두 대문자로 하되 밑줄로 각 단어를 분리한다.
goto MY_LABEL;

// ...

MY_LABEL:
	std::cout << "Magic!" << std::endl;
	return 0;

매크로 이름

  • 정말로 매크로를 정의하려고 하는가? 그렇다면 다음과 같이 하라. MY_MACRO_THAT_SCARES_SMALL_CHILDREN.
  • 상수의 이름은 모두 대문자로 하되 밑줄로 각 단어를 분리한다.

디버그

에러

  • clang++ 컴파일러 사용시 컴파일 옵션은 -Wall -Werror -Wextra
  • 모든 컴파일러 경고는 반드시 고친다.

Assert

  • 표준 C assert 대신에 자신만의 Assert 버전을 구현한다.
  • 특정 조건이 반드시 충족되어야 한다고 가정(assertion)하고 짠 코드 모든 곳에 assert를 사용한다.

미분류

  • 가능한 한 고정된 크기(size)의 컨테이너를 사용한다.

  • 동적 컨테이너를 사용해야 한다면 가능한 한 미리 reserve()를 호출한다.

  • 지역 객체를 반환할 때 NRVO의 이점을 활용한다. 이는 함수 내에 하나의 return문 만 쓴다는 것을 의미하며, 이것은 값으로 객체를 반환할 때만 적용된다.

  • structclass에서 초기화 후 값 변경을 막으려고 const 멤버 변수를 쓰지 않는다. 참조(&) 멤버변수의 경우도 마찬가지

  • 멤버 변수를 초기화할 때는 초기화 리스트를 사용하는 것을 기본으로 한다.

  • 변수 가리기(variable shadowing)는 허용되지 않는다. 외부 변수가 동일한 이름을 사용중이라면 내부 변수에는 다른 이름을 사용한다.

    class SomeClass
    {
    public:
        int32_t Count;
    public:
        void Func(const int32_t Count)
        {
            for (int32_t count = 0; count != 10; ++count)
            {
                // Use Count
            }
        }
    }
  • 한 줄에 변수 하나만 선언한다.

참고