0. 코딩 스탠다드를 지켜보자
코딩을 하다보니 이름 규칙이나 변수명 등을 정리하지 못하고 짓고 있는 것 같아서 이번 과제를 하면서는 코딩 스탠다드를 지켜보자는 생각에 C++ 코딩 스탠다드를 찾아보게 되었다.
가장 많이 참고한 곳은 https://docs.popekim.com/ko/coding-standards/cpp 이곳의 코딩 스탠다드이고, 언리얼 엔진의 코딩 스탠다드( https://dev.epicgames.com/documentation/ko-kr/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine?application_version=5.4 ) 또한 참고하였다.
1. 코딩 스탠다드는 왜 지켜야 할까
- 유지보수를 위해서 (가독성)
- 소프트웨어의 총 수명 비용 중 80%가 유지보수에 소모된다고 한다.
- 최초 작성자가 코드를 최초로 작성한 후, 코드의 수명이 다할때 까지 유지보수를 할 수 없다.
- 그렇기 때문에, 이후에 다른 엔지니어들이 해당 코드의 유지보수를 위해 코드의 가독성을 향상시키는 것이 비용을 줄이고, 효율성을 높일 수 있게 해준다.
- 기본 원칙
- 가독성이 최우선 (읽는 사람을 염두에 두고 작성하기), 코드는 그 자체가 문서의 역할을 한다.
- 문제가 있을 경우 최대한 빨리 크래시가 나고, assert에 걸리도록 한다.
- IDE의 자동 서식을 따른다.
- 참고)
- 파스칼 표기법: MyCharacter
- 카멜 표기법: myCharacter
2. 메인 코딩 표준
- 클래스와 구조체의 이름은 파스칼 표기법을 따른다.
- 지역변수, 함수의 매개변수의 이름은 카멜 표기법을 따른다.
- static 변수 앞에는 s 붙이기
- 메서드 이름은 동사-목적어 쌍으로 표기
- public 메서드는 파스칼 표기
- 그 외의 다른 메서드는 카멜 표기
- bool을 반환하는 메서드는 Is, Can, Has, Should를 사용하기
- bool형 변수에는 앞에 b 붙이기
- #define으로 정의된 함수의 이름은 대문자로, 밑줄로 단어 분리
- 네임스페이스는 소문자로
- 인터페이스는 앞에 I 붙이기
- 열거형(enum)을 선언할때 앞에 e 붙이기
- enum 대신 enum class 사용하기
- 클래스 멤버 변수명은 앞에 m 붙이기
- 값을 반환하는 함수는 함수의 이름만 봐도 무엇을 반환하는지 알게 짓기
- 반복문 안에서 i와 같은 변수보다, index 처럼 변수에 저장되는 데이터를 한눈에 알아볼 수 있는 변수명을 사용한다.
- 뒤에 추가적인 단어가 오지 않는다면, 줄임말을 대문자로 표기
- 클래스 멤버에 접근할 때는 setter와 getter 사용하기
- 헤더를 인클루드 할때
- 외부 헤더를 먼저 인클루드 한 후
- 내부 헤더를 인클루드 하기
- 인클루드 순서는 알파벳 순서를 따르기
- float 변수 뒤에는 f 붙여주기
- 원칙적으로 모든 곳에 const를 사용하기, 지역변수와 함수매개변수도 포함된다.
- 개체를 수정하지 않는 멤버 함수에는 const 붙이기 (getter 함수)
- 값(value) 형식의 변수를 const로 반환하지 않는다. 포인터나 참조(reference)를 반환할 경우에만 const 반환을 한다.
- const 값 반환의 의미는
- 리턴 값을 상수화 시키는 것 (수정 x)
- 이 내용을 아래 코드로 알아보자
- 어짜피 값 형식의 변수는 함수 호출 이후, 복사되기 때문에 독립적은 객체가 된다.
- 그러므로 참조나 포인터 반환의 경우 함수 호출된 후의 객체가 원래 객체이므로, const 반환을 하면 값의 수정이 불가능해진다.
- const 값 반환의 의미는
#include <iostream>
using namespace std;
class MyClass
{
public:
const int GetValue() const
{
return value;
}
const int& GetConstRefValue() const
{
return value;
}
private:
int value = 10;
};
int main()
{
MyClass obj;
int constCopyValue = obj.GetValue();
constCopyValue = 100; //수정할 수 있음 -> 의미 없는 const
const int& constCopyRefValue = obj.GetConstRefValue();
//constCopyRefValue = 100;
}
- 대부분의 경우 함수 오버로딩을 피한다.
//const Anim* GetAnim(const int index) const;
//const Anim* GetAnim(const char* name) const; // bad
const Anim* GetAnimByIndex(const int index) const;
const Anim* GetAnimByName(const char* name) const; //good
- 클래스는 각각 독립된 소스파일에 있어야 한다.
- 파일 이름은 대소문자까지 포함해서 클래스 이름과 일치해야 한다.
- 자신만의 assert 버전 구현하기
- 어떤 이유로든 매개변수로 nullptr로 넘어올 수 있는 경우가 아니라면, 포인터 대신 참조자 사용하자
- 함수의 매개변수를 통해 값이 반환될 때 포인터를 사용하며, 매개변수 이름 앞에 out 붙이기
void GetScreenDimension(uint32_t* const outWidth, uint32_t* const outHeight)
{
}
...
{
uint32_t width;
uint32_t height;
GetScreenDimension(&width, &height);
}
- 가능한 고정된 크기의 컨테이너를 사용하고, 동적 컨테이너를 쓸 때에는 가능한 미리 reserve() 호출하기
2. 최신 C++ 기능 관련
- 포인터에는 NULL 대신 nullptr 사용하기
- 개체의 수명이 클래스 내에서만 처리되는 경우 unique_ptr 사용하기
- 적용 가능한 곳이라면, 범위기반 for문을 사용하기
- 이동 생성자와 이동 대입 연산자를 사용해도 된다.
- 단순 상수 변수에는 const 대신 constexpr 사용하기
const int DEFAULT_SIZE = 65536; //bad
constexpr int DEFAULT_SIZE = 65536; //good
3. 언리얼 코딩 표준
- auto 사용하지 않기
- 이터레이터가 너무 장황해서 가독성에 악영향을 미칠 때는 auto 사용
- switch 문
- 맨 아래에 default 넣기
- 빈 case를 제외하면
- 모든 case 마다, 다른 케이스로 넘어간다는 것을 명시적으로 밝혀줘야함
- break를 넣거나 FALLTHROUGH 매크로 추가
- 포인터와 레퍼런스의 스페이스는 변수명 왼쪽에 있도록 한다.
- 이유는 빠르게 find in files를 사용할 수 있기 때문에
FShaderType* ptr; //good
FShaderType *ptr; //bad
FShaderType * ptr; //bad
'C++' 카테고리의 다른 글
C++ TIL day 10 (0) | 2024.12.30 |
---|---|
C++ 템플릿 - 헤더파일에서 구현하자 (0) | 2024.12.30 |
C++ TIL day 9 (3) | 2024.12.27 |
C++ TIL day 8 (0) | 2024.12.26 |
Smart Pointer 보충 (0) | 2024.12.24 |