C/C++ 코딩 스타일
개인적인 표준이자 프로젝트에 표준이 없을 때 제안하는 코딩 스타일이다.
헤더파일
#define 보호문
모든 헤더 파일에는 여러번 포함하지 않도록 #define
보호문을 사용한다.
1 |
<FILE>_H_ |
보호문 이름은 겹치지 않도록 파일 이름을 바탕으로 만든다.
1 2 3 4 |
#ifndef BAZ_H_ #define BAZ_H_ ... #endif // BAZ_H_ |
#pragma once
를 사용할 수도 있다.
1 |
#pragma once |
헤더 파일 의존성
객체 멤버 대신 참조나 포인터 또는 스마트 포인터 등으로 전방 선언을 할 수 있으면 #include
를 사용하지 않는다. 이 때문에 가독성이 나빠지고 성능이 떨어질 수도 있으므로 목적이 헤더 파일 포함을 최소화하는 것뿐이라면 이렇게 변경하지 않는다.
인라인 함수
함수 내용이 10 줄 이하일 때만 인라인으로 정의한다. 인라인으로 선언하더라도 항상 그렇게 되는 건 아니라는 점을 기억하자. 예를 들면 일반적으로 가상 또는 재귀 함수는 인라인하지 않는다.
이름 포함 순서
가독성을 높이고 숨은 의존성을 피하기 위해 다음 순서로 포함한다. 각 헤더 묶음 내에서는 알파벳 순서로 포함하고, 각 묶음은 빈 줄 하나로 구분한다.
- (필요하면) 프리컴파일 헤더
- 구현 파일의 원형/인터페이스 헤더 (cpp 파일에 해당하는
.h
파일) - 프로젝트에 속한 같은 디렉터리, 패키지 헤더
- 프로젝트에 속한 다른 패키지 헤더
- 프로젝트에 속한 공통 헤더
- 표준, 시스템에 속하지 않은 라이브러리 헤더 (Qt, Eigen 등)
- 거의 표준인 라이브러리 (Boost, Catch2 등)
- 플랫폼 헤더
- SDK 헤더
- OS 헤더
- 표준 C++ 헤더 (iostream 등)
- 표준 C 헤더 (cstdint, stdio.h 등)
프로젝트에 속한 헤더 파일을 포함하는 경로는 상대 경로 적을 때 사용하는 .
(현재 디렉터리) 또는 ..
(부모 디렉터리)을 사용하지 않고 해당 프로젝트의 소스 디렉터리 바로 아래부터 시작한다. 예를 들어 project/src/base/logging.h
파일은 다음처럼 적는다.
1 |
#include "base/logging.h" |
dir/foo.cpp
또는 dir/foo_test.cpp
파일이 dir2/foo2.h
내용을 구현하거나 테스트하는 것이라면 포함 순서는 다음과 같다.
- (필요하면) 프리컴파일 헤더
dir2/foo2.h
(선호하는 위치)- 프로젝트에 속한 같은 디렉터리, 패키지 헤더
- 프로젝트에 속한 다른 패키지 헤더
- 프로젝트에 속한 공통 헤더
- 표준, 시스템에 속하지 않은 라이브러리 헤더 (Qt, Eigen 등)
- 거의 표준인 라이브러리 (Boost, Catch2 등)
- 플랫폼 헤더
- SDK 헤더
- OS 헤더
- 표준 C++ 헤더 (iostream 등)
- 표준 C 헤더 (cstdint, stdio.h 등)
예를 들어 project/src/foo/internal/fooserver.cpp
파일에서 포함 순서는 다음과 같을 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#pragma once #include "foo/public/fooserver.h" // Preferred location. #include "foo/public/bar.h" #include "base/basictypes.h" #include "base/commandlineflags.h" #include <unordered_map> #include <vector> #include <sys/types.h> #include <unistd.h> |
범위
비멤버, 정적 멤버와 전역 함수
전역 함수보다는 네임스페이스 내 비멤버 함수나 정적 멤버를 우선 고려하자.
지역 변수
변수는 가능한 좁은 범위로 두며 선언할 때 초기화한다.
1 2 3 4 |
int i; i = f(); // Bad -- initialization separate from declaration. int j = g(); // Good -- declaration has initialization. |
for
, if
, while
문 등에서도 같다.
1 2 3 |
while (char const* p = strchr(str, '/')) { str = p + 1; } |
단, 그 변수가 객체이면 범위에 들어갈 때마다 생성자를 호출하고 범위를 벗어날 때마다 소멸자를 호출한다.
1 2 3 4 5 |
// Inefficient implementation: for (int i = 0; i < 1000000; ++i) { Foo f; // My ctor and dtor get called 1000000 times each. f.do_something(i); } |
이때는 루프 안에서 사용하는 객체를 해당 루프 밖에서 선언하는 게 더 효율적이다.
1 2 3 4 |
Foo f; // My ctor and dtor get called once each. for (int i = 0; i < 1000000; ++i) { f.do_something(i); } |
정적과 전역 변수
생성자에서 무언가를 소멸하는 클래스 타입 정적 또는 전역 변수는 금지한다. 생성자와 소멸자 순서가 정해져 있지 않아 찾기 힘든 버그를 만들 수 있다. 전역 변수, 정적 변수, 정적 클래스 멤버 변수 그리고 함수 정적 변수를 포함해 수명이 정적(static storage duration)인 객체는 POD 타입 또는 생성자에서 아무것도 하지 않는 클래스이어야 한다. 즉 int
, char
, float
등 기본 타입 또는 포인터나 배열, POD 구조체(생성자, 소멸자 등이 없는 C 구조체)나 소멸자에서 아무것도 하지 않는 클래스 객체여야 한다.
클래스
생성자에서 하는 일
일반적으로 생성자에서는 멤버 변수를 초기화하는 일만 하고 가상 메서드 호출이나 오류를 알리지 못하고 실패하는 초기화는 피한다.
구조체와 클래스
데이터를 저장만 하는 객체에는 구조체를 사용하고 그외에는 모두 클래스를 사용한다. 구조체에는 생성자, 소멸자, initialize()
, reset()
, validate()
등 데이터 멤버를 설정하는 데 사용하는 메서드 외 다른 것은 없어야 한다.
상속
상속보다는 포함이 더 낫다. 상속은 public
으로 한다. 구현 상속을 지나치게 사용하지 않으며 상속은 ‘is-a’ 관계일 때로 한정해 사용한다. protected
는 하위 클래스에서 접근할 수도 있는 멤버 함수로 제한해 사용한다. 데이터 멤버는 private
이어야 한다.
접근 제어
데이터 멤버는 private
으로 하고 필요하면 접근 제어자를 통해 접근하게 한다.
선언 순서
클래스 안에서 선언할 때는 public
다음에 private
, 메서드 다음에 데이터 멤버 등과 같은 순서로 한다. 클래스를 정의할 때는 public
, protected
, private
구역 순으로 하며 비어 있는 구역은 생략한다. 각 구역 안에서는 일반적으로 다음 순서로 한다.
using
선언,using
/typedef
타입 별칭,enum
- 상수 (
static const
데이터 멤버) - 생성자
- 소멸자
- 메서드 (정적 메서드 포함)
- 데이터 멤버 (
static const
데이터 멤버 제외)
함수
함수 매개 변수 순서
매개 변수 순서는 입력 전용, 입출력 겸용, 출력 전용 순이다. 함수 매개 변수 순서를 정할 때는 입력 전용 매개 변수 모두를 출력 전용 매개 변수 앞에 둔다. 특히 새 매개 변수를 추가한다는 이유로 함수 끝에 넣지 않으며, 새로 추가하는 입력 전용 매개 변수는 출력 매개 변수 앞에 둔다. 이는 관련 함수와 일관성 때문에 어겨야 할 수도 있다.
함수는 짧게
함수는 짧으면서 한 가지 일에 집중하게 한다. 때로는 긴 함수를 써야 할 수도 있으므로 길이를 강제하진 않으나 약 40 줄을 넘으면 프로그램 구조를 망가뜨리지 않으면서 내용을 나눌 수 없는지 생각해 본다.
명명 규칙
일반적인 명명 규칙
함수 이름, 변수 이름, 파일 이름은 설명적이어야 하므로 축약하지 않는다. 하지만 너무 긴 이름은 쓰기 어렵고 한 줄이 화면 안에 들어오지 않을 정도로 길어지므로 피한다. 타입과 변수는 명사, 함수는 ‘명령형’ 동사로 쓴다.
1 2 3 |
int price_count_reader; // No abbreviation. int num_errors; // "num" is a widespread convention. int num_dns_connections; // Most people know what "DNS" stands for. |
다음은 잘못된 예이다.
1 2 3 4 5 6 |
int n; // Meaningless. int nerr; // Ambiguous abbreviation. int n_comp_conns; // Ambiguous abbreviation. int wgc_connections; // Only your group knows what this stands for. int pc_reader; // Lots of things can be abbreviated "pc". int cstmr_id; // Deletes internal letters |
파일 이름
파일 이름은 모두 소문자여야 하고 밑줄을 포함할 수 있다. 사용할 수 있는 파일 이름은 다음과 같다.
1 2 3 |
my_useful_class.cpp myusefulclass.cpp myusefulclass_test.cpp // _unittest and _regtest are deprecated. |
C++ 파일은 .cpp
로, 헤더 파일은 .h
로 마친다. 파일 이름은 매우 한정적으로 만든다. 예를 들면 logs.h
보다는 http_server_logs.h
를 사용한다.
타입 이름
타입 이름은 대문자로 시작하고 새 단어를 시작할 때마다 대문자를 쓰며 밑줄은 사용하지 않는다. 클래스, 구조체, 타입 별칭, 열거형 등 모든 타입 이름은 명명 규칙이 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// classes and structs class UrlTable { ... class UrlTableTester { ... struct UrlTableProperties { ... // typedefs typedef hash_map<UrlTableProperties*, string> PropertiesMap; // using aliases using PropertiesMap = hash_map<UrlTableProperties*, string>; // enums enum UrlTableErrors { ... |
변수 이름
변수와 데이터 멤버 이름은 모두 소문자로 쓰고 단어 사이에 밑줄을 쓴다.
일반 변수 이름
1 2 3 4 |
string table_name; // OK string tablename; // OK string tableName; // Bad |
클래스 데이터 멤버
정적과 비정적 모두를 포함해 클래스의 private
데이터 멤버는 일반 비멤버 변수와 명명 규칙이 같으나 끝에 밑줄을 붙인다.
1 2 3 4 5 6 7 |
class TableInfo { ... private: string table_name_; // OK - underscore at end. string tablename_; // OK static Pool<TableInfo>* pool_; // OK. }; |
구조체 데이터 멤버
정적과 비정적 모두를 포함해 구조체의 public 데이터 멤버는 일반 비멤버 변수와 명명 규칙이 같으나 클래스 데이터 멤버와 달리 끝에 밑줄을 붙이지 않는다.
1 2 3 4 5 |
struct UrlTableProperties { string name; int num_entries; static Pool<UrlTableProperties>* pool; }; |
상수 이름
1 2 |
int const days_in_a_week = 7; constexpr int days_in_a_week = 7; |
함수 이름
함수는 소문자로 시작하고 각 단어 사이에 밑줄을 쓸 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
add_table_entry(); delete_url(); open_file_or_die(); class MyClass { public: ... bool is_empty() const { return num_entries_ == 0; } private: int num_entries_; }; |
네임 스페이스 이름
네임스페이스 이름은 모두 소문자이고 프로젝트 이름과 팀, 디렉터리 구조를 바탕으로 한다.
열거형 이름
열거형은 상수처럼 명명한다. 범위 비지정 열거형보다는 범위 지정 열거형을 우선한다.
1 2 3 4 5 6 7 8 9 10 11 |
enum class UrlTableErrors { ok = 0, error_out_of_memory, error_malformed_input }; enum UrlTableErrors { ok = 0, error_out_of_memory, error_malformed_input }; |
매크로 이름
매크로는 조건 컴파일 등 꼭 필요할 때 외에는 사용하지 않는다. 만약 꼭 사용해야 한다면 모두 대문자로 하고 단어는 밑줄로 구분한다.
1 |
#define PI_ROUNDED 3.0 |
형식
코딩 스타일과 형식은 매우 다양해서 모두가 이 규칙에 동의하지 않을 수 있다. 하지만 일관되게 사용하면 가독성을 높이는 데 도움이 된다. 형식을 정하는 기본 목표는 다음과 같다.
- 코드의 논리적 구조를 정확히 표현
- 코드의 논리적 구조를 일관되게 표현
- 가독성 향상
- 변경 사항에 대한 내성. 즉 코드 한 줄을 변경한다고 해서 여러 줄을 변경하지 않아야 한다.
줄 길이
각 줄은 최대 80 문자를 넘지 않게 한다. 예외는 다음과 같다.
- 예로 든 명령이나 URL이 있는 주석문이 80 문자를 넘을 때, 나누면 가독성이 나빠지거나 잘라 붙이기 또는 자동 연결을 쉽게 처리할 수 없을 때는 예외로 한다.
- 원시 문자열(Raw string) 리터럴은 80 문자를 넘을 수 있다.
#include
문은 80문자를 넘을 수 있다.- 헤더 보호문은 최대 길이를 넘을 수 있다.
비아스키(non-ascii) 문자
비아스키 문자는 사용하지 않으며 UTF-8 형식을 사용한다.
빈 칸과 탭
탭은 사용하지 않으며 빈 칸만 사용하고 한 번에 4칸을 들여 쓴다.
함수 선언과 정의
반환 타입은 함수 이름과 같은 줄에 쓰고 길이가 맞으면 매개 변수도 그렇게 한다.
1 2 3 4 5 |
ReturnType ClassName::function_name(Type par_name1, Type par_name2) { // OK do_something(); ... } |
한 줄에 모두 쓰지 못하면 다음처럼 작성해 본체와 쉽게 구분할 수 있게 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ReturnType LongClassName::really_really_really_long_function_name( Type par_name1, Type par_name2, Type par_name3) // 4 space indent { do_something(); // 4 space indent ... } ReturnType LongClassName::really_really_really_long_function_name( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { do_something(); // 4 space indent ... } |
주의할 몇 가지는 다음과 같다.
- 매개 변수 이름을 잘 선택한다.
- 매개 변수를 사용하지 않으며 목적이 명확하면 이름을 생략할 수 있다.
- 반환 타입과 함수 이름을 한 줄에 둘 수 없으면 둘 사이에서 나눈다.
- 함수 선언이나 정의의 반환 타입 다음에서 나눌 때는 들여 쓰지 않는다.
- 여는 괄호는 항상 함수 이름과 같은 줄에 둔다.
- 함수 이름과 여는 괄호 사이는 띄우지 않는다.
- 괄호와 매개 변수 사이는 띄우지 않는다.
- 여는 중괄호는 해당 함수 선언의 다음 줄에서 시작한다.
- 닫는 중괄호는 여는 중괄호와 같은 줄에 또는 함수 본체 마지막 줄에 둔다.
사용하지 않는 매개 변수 의미가 문맥상 명확하면 생략할 수 있다.
1 2 3 4 5 6 7 |
class Foo { public: Foo(Foo&&) = default; Foo(Foo const&) = default; Foo& operator=(Foo&&) = default; Foo& operator=(Foo const&) = default; }; |
사용하지 않으나 매개 변수 의미를 명확히 표현하고 싶으면 함수 정의에서 해당 변수 이름을 주석 처리한다.
1 2 3 4 5 6 7 8 9 10 11 |
class Shape { public: virtual void rotate(double radians) = 0; }; class Circle : public Shape { public: void rotate(double radians) override; }; void Circle::rotate(double /*radians*/) {} |
1 2 3 4 |
// Bad - if someone wants to implement later, it's not clear what the // variable means. void Circle::rotate(double) {} |
람다 표현식
매개 변수와 본체는 다른 함수와, 갈무리(capture) 목록은 쉼표로 구분한 목록과 형식이 같다. 참조로 갈무리할 때는 앰퍼샌드(&)와 변수 이름 사이를 띄우지 않는다.
1 2 |
int x = 0; auto add_to_x = [&x](int n) {x += n;}; |
짧은 람다는 함수 인자로 인라인할 수 있다.
1 2 3 4 5 6 7 8 |
set<int> black_list = {7, 8, 9}; vector<int> digits = {3, 9, 1, 8, 4, 7, 1}; digits.erase( std::remove_if( digits.begin(), digits.end(), [&black_list](int i) {return black_list.end() != black_list.find(i);}), digits.end()); |
함수 호출
호출문 전체를 한 줄로 작성하거나, 인자를 줄바꿈 후 4 칸 들여 쓰기한 다음 작성한다. 여는 괄호와 닫는 괄호 앞에는 빈 칸을 두지 않는다. 호출 형식은 다음과 같다.
1 |
bool result = do_something(argument1, argument2, argument3); |
호출문 전체를 한 줄로 작성할 수 없으면 인자를 줄바꿈 후 4 칸 들여 쓴다.
1 2 |
bool result = do_something( argument1, argument2, argument3); |
줄바꿈 후에도 모든 인자를 한 줄에 쓸 수 없으면 4 칸 들여 쓰기하고 인자를 한 줄에 하나씩 쓴다.
1 2 3 4 |
bool result = do_something( argument1, argument2, argument3); |
긴 표현식
긴 표현식은 연산자 앞에서 줄바꿈 후 4 칸 들여 쓴다.
1 2 3 4 5 6 7 8 9 10 |
force = gravitational_constant * mass1 * mass2 / (distance * distance); force = gravitational_constant * mass1 * mass2 / (distance * distance); |
조건문
괄호 안에는 빈 칸을 두지 않고 if
와 else
는 서로 다른 줄에 둔다.
1 2 3 4 5 6 7 |
if (condition) { // no spaces inside parentheses ... // 4 space indent. } else if (...) { // The else goes on the same line as the closing brace. ... } else { ... } |
if
와 여는 괄호 사이, 닫는 괄호와 여는 중괄호 사이는 반드시 띄운다.
1 2 3 |
if(condition) { // Bad - space missing after IF. if (condition){ // Bad - space missing before {. if(condition){ // Doubly bad. |
짧은 조건문이라도 한 줄에 작성하지 않는다. 조건 이후 단일 문장이 올 때는 중괄호가 필수는 아니나 사용해 가독성을 높일 수 있다.
1 2 3 4 5 6 7 8 |
if (foo == x) return new Foo(); // Bad if (foo == x) return new Foo(); // Good if (bar == x) { // Good return new Bar(); } |
if-else
문에서 중괄호는 if
와 else
모두에 사용하거나 사용하지 않아야 하며 한 쪽만 사용해서는 안 된다.
1 2 3 4 5 6 7 8 9 10 |
if (bar == x) { // Good return new Bar(); } else { return new Foo(); } if (bar == x) { // Bad return new Bar(); } else return new Foo(); |
루프와 switch 문
switch
문 각 구역에 중괄호를 사용할 수 있다. 단일 문장 루프에서 중괄호는 선택적이다. 루프 본체가 비어 있을 때는 {}
나 continue
를 사용한다.
switch
문 내 각 case
구역에는 중괄호를 쓰거나 그러지 않을 수 있다. 사용한다면 아래처럼 한다. 열거 값에 해당하지 않는 조건이 있을 때는 항상 default
를 둬야 한다. 절대 default
사례를 실행하지 않아야 한다면 assert
를 사용한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
switch (var) { case 0: { // 4 space indent ... // 8 space indent break; } case 1: { ... break; } default: { assert(false); } } |
단일 문장 루프에서 중괄호는 선택적이다.
1 2 3 4 5 6 |
for (int i = 0; some_number != i; ++i) { printf("I take it back\n"); } for (int i = 0; some_number != i; ++i) printf("I take it back\n"); |
본체가 비어 있으면 {}
나 continue
를 사용하고 세미콜론 하나만 사용하지 않는다.
1 2 3 4 5 6 |
while (condition) { // Repeat test until it returns false. } for (int i = 0; some_number != i; ++i) {} // Good - empty body. while (condition) continue; // Good - continue indicates no logic |
1 2 3 4 |
while (condition); // Bad - looks like part of do/while loop. while (condition) { ; // Bad - looks like part of do/while loop. } |
포인터와 참조 표현식
마침표나 화살표 주위에는 빈 칸을 두지 않는다. 포인터 연산자 다음에는 빈 칸을 두지 않는다.
1 2 3 4 |
x = *p; p = &x; x = r.y; x = r->y; |
다음을 주의한다.
- 멤버에 접근할 때 마침표나 화살표 연산자 주위에 빈 칸을 두지 않는다.
- 포인터 연산자에서
*
와&
다음에 빈 칸을 두지 않는다.
포인터/참조 변수나 인자를 선언할 때는 연산자를 타입에 붙여 쓴다.
1 2 |
char* c; string const& str; |
부울 표현식
부울 표현식이 표준 줄 길이보다 더 길면 연산자를 줄 시작 부분에 두도록 나눈다. 괄호를 적절히 사용해 가독성을 높인다.
1 2 3 4 5 |
if (this_one_thing > this_other_thing && a_third_thing == a_fourth_thing // 4 space indent && yet_another && last_one) { ... // 4 space indent } |
반환값
return
문을 불필요하게 괄호로 감싸지 않는다.
1 2 3 4 |
return result; // No parentheses in the simple case. // Parentheses OK to make a complex expression more readable. return (some_long_condition && another_condition); |
전저리기 지시자
전처리기 지시자를 시작하는 해시 표시는 항상 줄 처음에 둔다. 전처리기 지시자가 들여 쓰기한 코드 본체 안에 있더라도 지시자는 줄 처음에서 시작한다.
1 2 3 4 5 6 7 8 9 10 |
// Good - directives at beginning of line if (lopsided_score) { #if DISASTER_PENDING // Correct -- Starts at beginning of line drop_everything(); # if NOTIFY // OK but not required -- Spaces after # notify_client(); # endif #endif back_to_normal(); } |
1 2 3 4 5 6 7 |
// Bad - indented directives if (lopsided_score) { #if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line drop_everything(); #endif // Wrong! Do not indent "#endif" back_to_normal(); } |
클래스 형식
public
, protected
, private
순으로 하며 선언 기본 형식은 다음과 같다.
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 |
class MyClass : public OtherClass { public: MyClass(); // Regular 4 space indent. explicit MyClass(int var); ~MyClass() {} void some_function(); void some_function_that_does_nothing() {} void set_some_var(int var) { some_var_ = var; } int some_var() const { return some_var_; } private: bool some_internal_function(); int some_var_; int some_other_var_; }; |
다음을 주의한다.
- 모든 기초 클래스 이름은 하위 클래스 이름과 같은 줄에 두며 80 문자 제한을 따른다.
public
,protected
,private
키워드는 들여 쓰지 않는다.- 이 키워드 앞에서는 한 줄 띄운다. 단 처음 사용한 것에는 적용하지 않는다.
- 이 키워드 다음에서는 띄우지 않는다.
public
구역을 처음으로 하고protected
,private
구역 순으로 한다.- 각 구역 안에서 선언 순서는 선언 순서 규칙을 참고한다.
생성자 초기화 목록
생성자 초기화 목록은 모두 한 줄에 쓰거나 다음 줄에 4 칸 들여 쓴다.
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 |
// When everything fits on one line: MyClass::MyClass(int var) : some_var_{var} { do_something(); } // If the signature and initializer list are not all on one line, // you must wrap before the colon and indent 4 spaces: MyClass::MyClass(int var) : some_var_{var}, some_other_var_{var + 1} { do_something(); } // When the list spans multiple lines, put each member on its own line // and align them: MyClass::MyClass(int var) : some_var_{var}, // 4 space indent some_other_var_{var + 1} // lined up { do_something(); } // As with any other code block, the close curly can be on the same // line as the open curly, if it fits. MyClass::MyClass(int var) : someVar_{var} {} |
네임스페이스 형식
네임스페이스 안 내용은 들여 쓰기 한다.
1 2 3 4 5 6 7 8 |
namespace { void foo() { ... } } // namespace |
중첩 네임스페이스를 선언할 때도 들여 쓴다.
1 2 3 |
namespace foo { namespace bar { |
수평 빈 칸
일반
줄 끝에는 공백 문자를 두지 않으며 이미 있으면 제거한다.
1 2 3 4 5 6 7 8 9 10 |
int i = 0; // Semicolons usually have no space before them. // No spaces inside braces for braced-init-list. int x[] = {0}; // Spaces around the colon in inheritance and initializer lists. class Foo : public Bar { public: Foo(int b) : Bar{}, baz_{b} {} // No spaces inside empty braces. ... |
루프와 조건
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
if (b) { // Space after the keyword in conditions and loops. } else { // Spaces around else. } while (test) {} // There is usually no space inside parentheses. switch (i) { for (int i = 0; 5 != i; ++i) { // For loops always have a space after the semicolon. for (; 5 != i; ++i) { ... // Range-based for loops always have a space before and after the colon. for (auto x : counts) { ... } switch (i) { case 1: // No space before colon in a switch case. ... case 2: break; |
연산자
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Assignment operators always have spaces around them. x = 0; // Other binary operators usually have spaces around them. // Parentheses should have no internal padding. v = w * x + y / z; v = w * (x + z); // No spaces separating unary operators and their arguments. x = -5; ++x; if (x && !y) ... |
템플릿과 변환
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// No spaces inside the angle brackets (< and >), before < vector<string> x; y = static_cast<char*>(x); vector<char*> x; set<list<string>> x; // Permitted in C++11 code. set<list<string> > x; // C++03 required a space in > >. // Space between template keyword and < template <typename T> void foo() { ... } |
빈 줄
필요 없이 빈 줄을 사용하지 않는다. 특히 함수 사이에는 한 줄이나 두 줄만 띄운다. 함수를 빈 줄로 시작하거나 빈 줄로 마치지 않으며 함수 안에서는 잘 판단해 사용한다. 기본 원칙은 코드를 한 화면 안에 나오게 하면 프로그램 제어 흐름을 따라가고 이해하기 더 쉽다는 것이다. 물론 너무 밀집하거나 퍼뜨려도 가독성이 나빠질 수 있으므로 잘 판단해야 한다. 하지만 일반적으로 빈 줄은 최소로 사용한다.
형식 설정 파일
Visual Studio와 JetBrains IDE용 형식 설정 파일은 다음에서 찾을 수 있다.