2. 언리얼 C++ 표준

TechN0·2025년 2월 6일

코딩 / 프로젝트 표준?

쉽게 말하면 코딩 스타일

구글 코딩 표준

https://google.github.io/styleguide/cppguide.html

언리얼 코딩 표준

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine?application_version=5.1

표준을 왜 따라야하지?

작성된 코드는 한 사람이 만든 것 처럼 통일된 방식으로 작성되어야 코드를 분석하는 시간을 줄여 프로젝트 효율을 높임

소프트웨어 총 수명 비용중 80%가 유지 보수에 사용된다 함

클래스 체계

읽는 사람을 염두로

퍼블릭 인터페이스에서 먼저 선언 후 클래스의 프라이빗 구현

class HELLOU_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()
public: // 퍼블릭 먼저

private: // 그 다음 프라이빗 
};

저작권 고지

단순히 카피라이트 주석같은거 지우지 말라는거

명명 규칙

  • 파스칼 케이싱: 합성어 첫 글자를 대문자로

    • UnrealEngin
  • 카멜 케이싱: 첫 합성어는 소문자, 나머지는 대문자

    • unrealEngin
  • 스네이크 케이싱: 합성어 사이에 언더바

    • unreal_engin

언리얼에서는 파이스칼 케이싱 사용해야 함

  • UObject 에서 상속받는 클래스에는 접두사 U 포함
  • 하지만 예외로 AActor 에서 상속받는 클래스에는 접두사 A 포함
  • SWidget 에서 상속받는 클래스에는 접두사 S 포함
  • 추상적 인터페이스인 클래스에는 접두사 I 포함
  • 부울 변수는 접두사 b 포함(소문자 맞음)

더 자세한건 위에 언리얼 문서 링크로

  • 부울을 반환하는 모든 함수는 IsVisible() 또는 ShouldClearBuffer() 등의 true/false 질문으로

포터블 C++ 코드

  • bool - 부울 값(부울 크기 추정 금지). BOOL 은 컴파일되지 않음
  • TCHAR - character(문자) (TCHAR 크기 추정 금지)
  • uint8 - unsigned byte(부호 없는 바이트) (1바이트)
  • int8 - signed byte(부호 있는 바이트) (1바이트)
  • uint16 - unsigned 'shorts'(부호 없는 'short') (2바이트)
  • int16 - signed 'short'(부호 있는 'short')(2바이트)
  • uint32 - unsigned int(부호 없는 int) (4바이트)
  • int32 - signed int(부호 있는 int) (4바이트)
  • uint64 - unsigned 'quad word'(부호 없는 '쿼드 단어') (8바이트)
  • int64 - signed 'quad word'(부호 있는 '쿼드 단어') (8바이트)
  • float - 단정밀도 부동 소수점(4바이트)
  • double - 배정밀도 부동 소수점(8바이트)
  • PTRINT - 포인터를 가질 수 있는 정수(PTRINT 크기 추정 금지)

게임엔진은 메모리를 효율적으로 사용해야하기 때문(메모리 크기파악이 중요)

C++ 표준라이브러리 사용 금지

너무 범용적이니 알맞지 않다.

언리얼꺼 쓰라는 뜻

Const 정확도

cosnt 쓸수 있는 변수면 const쓰는게 좋다.

그래야 협업할 때 아 이건 변경하면 안되는 인자구나 인지할 수 있음

루프문에서 값이 변경되면 루프 데이터가 깨질수 있으니 루프문에서는 엘리먼트에 const로 지정해주는게 좋다.(물론 예외사항도 있다. 보통 이 경우 주석을 단다.)

void SomeMutatingOperation(FThing& OutResult, const TArray<Int32>& InArray)
	{
		// InArray는 여기서 수정되지 않지만, OutResult는 수정될 수도 있습니다.
	}

	void FThing::SomeNonMutatingOperation() const
	{
		// 이 코드는 자신을 호출한 FThing을 수정하지 않습니다.
	}

	TArray<FString> StringArray;
	for (const FString& : StringArray)
	{
		// 이 루프의 바디는 StringArray를 수정하지 않습니다.
	}

포인터에 증감연산자 사용금지 할때 cosnt 를 쓸 수도 있음

포인터가 가르키는 데이터는 const 가 아니기 때문에 변경 가능

// const 포인터에서 const 이외 오브젝트 - 포인터로의 재할당은 불가하나, T는 여전히 수정 가능합니다.
	T* const Ptr = ...;

	// 틀림
	T& const Ref = ...;

레퍼런스 자체가 수정 불가기 때문에 레퍼런스에는 const 안쓴다.

nullptr

C스타일 NULL대신 C++의 nullptr을 써서 널 체크 해야함

Auto키워드

몇 가지 예외를 제외하면 auto키워드 사용하지 마라

  • 람다 사용 시
  • 이터레이터 사용시 너무 장황해 가독성에 악영향을 미치는 경우

이터레이터: 컨테이너(배열, 리스트, 맵 등)의 요소를 순회(Iterate)하는 객체 또는 포인터

TArray<int32> Numbers = {1, 2, 3, 4, 5};

// auto 사용 전
for (TArray<int32>::TIterator It = Numbers.CreateIterator(); It; ++It) {
    UE_LOG(LogTemp, Log, TEXT("%d"), *It);
}

// auto 사용 후
for (auto It = Numbers.CreateIterator(); It; ++It) {
    UE_LOG(LogTemp, Log, TEXT("%d"), *It);
}

범위기반 for

TMap<FString, int32> MyMap;

	// 기존 스타일
	for (auto It = MyMap.CreateIterator(); It; ++It)
	{
		UE_LOG(LogCategory, Log, TEXT("Key: %s, Value: %d"), It.Key(), *It.Value());
	}

	// 새 스타일
	for (TPair<FString, int32>& Kvp : MyMap)
	{
		UE_LOG(LogCategory, Log, TEXT("Key: %s, Value: %d"), *Kvp.Key, Kvp.Value);
	}

Enum

Enum 클래스는 항상 일반 열거형이든 UENUM 이든 기존 네임스페이스 열거형을 대체하여 사용

// 기존 열거형
	UENUM()
	namespace EThing
	{
		enum Type
		{
			Thing1,
			Thing2
		};
	}

	// 새 열거형
	UENUM()
	enum class EThing : uint8
	{
		Thing1,
		Thing2
	} 

물리적 종속

  • 파일 이름에는 가급적 접두사 붙이지 않기
  • 모든 헤더는 #pragma once 지시어로 중복된 헤더를 다시 복사하지 않게 해야 함

캡슐화

  • 될수있으면 프라이빗으로 선언 해 getter setter함수를 통해 접근할수 있게
  • 더 이상 파생시킬 클래스가 아니면 final 사용해 잠궈라

일반적 스타일 문제

  • 포인터와 레퍼런스의 스페이스는 그 오른쪽에 딱 한 칸만
  • 그래야 특정 타입에 대한 모든 포인터나 레퍼런스에 빠르게 Find in Files 사용가능

포인터 선언시 아래처럼

	FShaderType* Ptr

나쁜 예

FShaderType *Ptr
FShaderType * Ptr
  • 헤더에 특수한 스태틱 변수를 정의하지 않도록
  • 해당 헤더가 포함된 모든 이동 단위로 인스턴스가 컴파일되기 때문
// SomeModule.h
static const FString GUsefulNamedString = TEXT("String");Copy full snippet

이러한 코드는 다음으로 대체해야 함

// SomeModule.h
extern SOMEMODULE_API const FString GUsefulNamedString;
// SomeModule.cpp
const FString GUsefulNamedString = TEXT("String");Copy full snippet

0개의 댓글