0.모던 C++의 개념
"모던 C++"은 일반적으로 C++11 표준 이후에 추가된 새로운 기능과 문법, 스타일을 포함한 C++의 최신 발전을 지칭하는 용어예요. C++는 1983년에 처음 만들어졌고, 이후 계속해서 발전해 왔는데, 특히 C++11 이후부터는 언어 자체가 크게 변화하며 현대적인 개발을 위한 많은 기능들이 추가되었어요. 이때부터의 변화를 묶어서 "모던 C++"이라고 부르는 거죠.
1. 자동 타입 추론 (auto
, decltype
)
auto
키워드를 통한 타입 추론과decltype
을 활용한 변수의 타입 추론.
- 타입 추론을 통해 코드의 간결성과 가독성 높이기.
예제 코드 및 설명
#include <iostream>
#include <vector>
int main() {
auto x = 10; // int로 추론
auto y = 3.14; // double로 추론
std::vector<int> vec = {1, 2, 3};
for (auto& elem : vec) { // 타입 추론을 통한 범위 기반 for 루프
std::cout << elem << " ";
}
return 0;
}
auto
는 변수의 타입을 컴파일러가 추론하도록 도와주며, 코드의 간결성을 높여줍니다.
- Unreal Engine에서 자동 타입 추론은 특히 블루프린트와 C++ 간 데이터 타입을 쉽게 관리할 때 유용합니다.
Unreal Engine 예제 코드
TArray<int32> MyArray = {1, 2, 3, 4};
for (auto& Elem : MyArray) {
UE_LOG(LogTemp, Warning, TEXT("Element: %d"), Elem);
}
- Unreal Engine의
TArray
와 같은 컨테이너에서auto
를 사용하여 요소의 타입을 자동으로 추론하고 쉽게 접근할 수 있습니다.
2. 스마트 포인터 (std::unique_ptr
, std::shared_ptr
)
- *
std::unique_ptr
*와 **std::shared_ptr
*의 차이점과 사용법.
- 메모리 관리를 더 안전하게 하고 RAII (Resource Acquisition Is Initialization) 개념 이해.
예제 코드 및 설명
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "Constructor called\n"; }
~MyClass() { std::cout << "Destructor called\n"; }
};
int main() {
std::unique_ptr<MyClass> uniquePtr = std::make_unique<MyClass>();
std::shared_ptr<MyClass> sharedPtr1 = std::make_shared<MyClass>();
std::shared_ptr<MyClass> sharedPtr2 = sharedPtr1; // 참조 횟수 증가
return 0;
}
- *
std::unique_ptr
*는 소유권을 단일 객체에만 부여하며, 다른 포인터로 소유권을 이전할 수 없습니다.
- *
std::shared_ptr
*는 여러 개의 포인터가 동일한 객체를 참조할 수 있게 하여, 참조 횟수를 관리합니다.
- Unreal Engine에서 스마트 포인터는 메모리 관리에 중요한 역할을 하며, 특히
TSharedPtr
같은 언리얼 전용 스마트 포인터는 객체 수명 관리에 필수적입니다.
Unreal Engine 예제 코드
TSharedPtr<MyClass> MySharedPtr = MakeShareable(new MyClass());
if (MySharedPtr.IsValid()) {
UE_LOG(LogTemp, Warning, TEXT("Shared pointer is valid"));
}
TSharedPtr
은 Unreal Engine에서 객체의 메모리를 안전하게 관리하기 위해 사용됩니다.
3. 범위 기반 for 루프
- C++11부터 도입된 새로운 for 루프 형태.
- 벡터, 리스트 등의 컨테이너를 순회하며 코드 간결화.
예제 코드 및 설명
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (const auto& num : numbers) {
std::cout << num << " ";
}
return 0;
}
- 범위 기반 for 루프는 컨테이너의 모든 요소를 쉽게 순회할 수 있게 해줍니다.
- Unreal Engine에서 TArray와 같은 컨테이너를 다룰 때 코드의 간결성을 높이는 데 유용합니다.
Unreal Engine 예제 코드
TArray<FString> Names = {TEXT("Alice"), TEXT("Bob"), TEXT("Charlie")};
for (const auto& Name : Names) {
UE_LOG(LogTemp, Warning, TEXT("Name: %s"), *Name);
}
TArray
와 같은 Unreal 컨테이너를 순회할 때 범위 기반 for 루프를 사용하여 코드를 간결하게 유지할 수 있습니다.
4. 람다 함수
- 람다 함수의 기본 문법, 캡처 리스트, 반환 타입 유추.
- 함수 객체와의 차이점 이해, 코드의 유연성과 간결성을 높이는 방법.
예제 코드 및 설명
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
int factor = 2;
std::for_each(vec.begin(), vec.end(), [factor](int& n) { n *= factor; });
for (const auto& n : vec) {
std::cout << n << " ";
}
return 0;
}
- 람다 함수는 코드 블록을 간결하게 작성할 수 있게 도와주며, 특정 변수들을 캡처하여 사용할 수 있습니다.
- Unreal Engine에서는 콜백 함수나 블루프린트와의 상호작용을 간단히 구현하는 데 유용하게 사용됩니다.
Unreal Engine 예제 코드
TArray<int32> Values = {1, 2, 3, 4, 5};
int32 Multiplier = 3;
Values.Sort([Multiplier](int32 A, int32 B) {
return (A * Multiplier) < (B * Multiplier);
});
- 람다 함수를 사용하여 Unreal Engine의 배열을 정렬하거나 특정 조건에 따라 데이터를 처리할 수 있습니다.
5. 움직임 의미론 (Move Semantics)와 R값 참조 (&&
)
- 복사 vs 이동: R값 참조(
&&
)를 이용한 이동 생성자와 이동 할당 연산자.
std::move
를 통해 객체의 소유권 이전 이해.
예제 코드 및 설명
#include <iostream>
#include <vector>
class MyClass {
public:
MyClass() { std::cout << "Constructor\n"; }
MyClass(const MyClass&) { std::cout << "Copy Constructor\n"; }
MyClass(MyClass&&) noexcept { std::cout << "Move Constructor\n"; }
};
int main() {
MyClass a;
MyClass b = std::move(a); // 이동 생성자 호출
return 0;
}
- 이동 의미론은 불필요한 복사를 줄여 성능을 최적화할 수 있습니다.
- Unreal Engine에서 대규모 데이터를 처리할 때 객체를 복사하는 대신 이동하여 성능을 높이는 데 유용합니다.
Unreal Engine 예제 코드
TArray<int32> SourceArray = {1, 2, 3, 4};
TArray<int32> DestinationArray = MoveTemp(SourceArray);
UE_LOG(LogTemp, Warning, TEXT("SourceArray Num: %d"), SourceArray.Num());
UE_LOG(LogTemp, Warning, TEXT("DestinationArray Num: %d"), DestinationArray.Num());
MoveTemp
를 사용하여TArray
의 데이터를 효율적으로 이동할 수 있습니다.
6. constexpr
와 상수 표현
- *
constexpr
*와 **const
*의 차이점.
- 컴파일 시간 계산의 이점과 최적화.
예제 코드 및 설명
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int value = square(5); // 컴파일 시간에 계산됨
std::cout << value << std::endl;
return 0;
}
constexpr
는 컴파일 시간에 계산이 가능한 상수를 정의하여 프로그램의 성능을 높입니다.
- Unreal Engine에서 상수값이 자주 사용되는 경우, 컴파일 시간에 계산하도록
constexpr
를 활용하여 최적화할 수 있습니다.
Unreal Engine 예제 코드
constexpr int32 MaxHealth = 100;
void AMyCharacter::BeginPlay() {
Super::BeginPlay();
CurrentHealth = MaxHealth;
UE_LOG(LogTemp, Warning, TEXT("Character Health: %d"), CurrentHealth);
}
constexpr
를 사용하여 캐릭터의 최대 체력 등 상수값을 정의하고 최적화할 수 있습니다.
7. 가변 인자 템플릿 (Variadic Templates)
- 템플릿의 기본 개념부터 가변 인자를 다루는 방법.
- 재귀적으로 가변 인자 템플릿을 처리하는 방식 이해.
예제 코드 및 설명
#include <iostream>
void print() {
std::cout << "\n";
}
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first << " ";
print(args...);
}
int main() {
print(1, 2, 3, 4, "Hello");
return 0;
}
- 가변 인자 템플릿은 다양한 인자의 개수를 처리할 수 있어 코드의 유연성을 높여줍니다.
- Unreal Engine에서는 다양한 타입의 인자를 받아 처리하는 함수 작성에 유용합니다.
Unreal Engine 예제 코드
template<typename... Args>
void LogMultiple(Args... args) {
(UE_LOG(LogTemp, Warning, TEXT("%s"), *args), ...);
}
LogMultiple(TEXT("Hello"), TEXT("Unreal"), TEXT("Engine"));
- 가변 인자 템플릿을 사용하여 여러 개의 문자열을 한 번에 로그 출력할 수 있습니다.
8. 스레드와 동기화 (std::thread
, std::mutex
)
- *
std::thread
*를 이용한 멀티스레드 프로그래밍 기본.
std::mutex
, **std::lock_guard
*를 통한 스레드 동기화 기법 이해.
예제 코드 및 설명
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread(int id) {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Thread " << id << " is running\n";
}
int main() {
std::thread t1(print_thread, 1);
std::thread t2(print_thread, 2);
t1.join();
t2.join();
return 0;
}
- 멀티스레드 프로그래밍은 병렬 처리를 통해 성능을 향상시킬 수 있으며, **
std::mutex
*로 데이터의 동기화를 관리합니다.
- Unreal Engine에서는 게임 로직의 비동기 처리나 물리 연산 등에 스레드를 사용할 수 있습니다.
Unreal Engine 예제 코드
FCriticalSection Mutex;
void AMyActor::ThreadSafeFunction() {
FScopeLock Lock(&Mutex);
// 안전하게 액세스 가능한 코드 블록
UE_LOG(LogTemp, Warning, TEXT("Thread-safe operation"));
}
FCriticalSection
과FScopeLock
을 사용하여 스레드 간 안전한 데이터 접근을 구현할 수 있습니다.
9. 표준 라이브러리 컨테이너의 확장 (std::array
, std::unordered_map
)
- 새로운 컨테이너 타입들 이해:
std::array
,std::unordered_map
.
- 언제 어떤 컨테이너를 사용하는 것이 좋은지 이해.
예제 코드 및 설명
#include <iostream>
#include <array>
#include <unordered_map>
int main() {
std::array<int, 3> arr = {1, 2, 3};
std::unordered_map<std::string, int> umap = { {"A", 1}, {"B", 2} };
for (const auto& elem : arr) {
std::cout << elem << " ";
}
std::cout << std::endl;
for (const auto& pair : umap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
- *
std::array
*는 고정 크기 배열을 사용하며, **std::unordered_map
*은 해시 테이블을 사용하여 빠른 검색을 제공합니다.
- Unreal Engine에서 데이터 매핑이 필요한 경우,
TMap
같은 언리얼 컨테이너와 유사한 개념입니다.
Unreal Engine 예제 코드
TMap<FString, int32> NameAgeMap;
NameAgeMap.Add(TEXT("Alice"), 25);
NameAgeMap.Add(TEXT("Bob"), 30);
for (const auto& Elem : NameAgeMap) {
UE_LOG(LogTemp, Warning, TEXT("Name: %s, Age: %d"), *Elem.Key, Elem.Value);
}
TMap
을 사용하여 키-값 쌍으로 데이터를 저장하고 빠르게 검색할 수 있습니다.
10. std::chrono
를 이용한 시간 관리
- C++에서 시간 측정을 위한
std::chrono
사용법.
- 시간 단위 변환, 코드의 성능 측정 예제.
예제 코드 및 설명
#include <iostream>
#include <chrono>
#include <thread>
int main() {
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::seconds(1));
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
std::cout << "Elapsed time: " << elapsed.count() << " seconds" << std::endl;
return 0;
}
- *
std::chrono
*를 사용하면 코드 실행 시간을 측정하거나 특정 시간 동안 지연시킬 수 있습니다.
- Unreal Engine에서 성능 측정이나 시간 기반 이벤트를 관리할 때 유용하게 사용됩니다.
Unreal Engine 예제 코드
float StartTime = FPlatformTime::Seconds();
FPlatformProcess::Sleep(1.0f);
float EndTime = FPlatformTime::Seconds();
UE_LOG(LogTemp, Warning, TEXT("Elapsed time: %f seconds"), EndTime - StartTime);
FPlatformTime
과FPlatformProcess
를 사용하여 시간 측정 및 지연을 구현할 수 있습니다.
11. 예외 안전성 (noexcept
, 스마트 포인터와 예외 처리)
- 함수의 예외를 명시적으로 막기 위한
noexcept
.
- 예외 안전한 코드 작성 방법과 스마트 포인터의 역할.
예제 코드 및 설명
#include <iostream>
void safeFunction() noexcept {
std::cout << "This function is noexcept" << std::endl;
}
int main() {
safeFunction();
return 0;
}
- *
noexcept
*는 예외를 던지지 않는 함수임을 명시하여 컴파일러 최적화를 도와줍니다.
- Unreal Engine에서는 예외 처리를 제한적으로 사용하기 때문에, **
noexcept
*를 사용해 코드 안정성을 높이는 것이 중요합니다.
Unreal Engine 예제 코드
void AMyActor::MyFunction() noexcept {
// 이 함수는 예외를 던지지 않음을 명시
UE_LOG(LogTemp, Warning, TEXT("This function is noexcept"));
}
- Unreal Engine에서는 예외를 사용하지 않는 것이 권장되므로, 예외가 발생하지 않는 함수에 대해
noexcept
를 사용하여 코드의 신뢰성을 높일 수 있습니다.
12. 모던 C++ 스타일 가이드
- 범위 기반 for 루프, 스마트 포인터 사용 등 모던 C++ 스타일의 권장 사항.
- 코드 품질을 높이기 위한 최적의 모던 C++ 사용법.
예제 코드 및 설명
- 스마트 포인터 사용: **
std::unique_ptr
*와 **std::shared_ptr
*을 통해 명시적인 메모리 관리.
- 범위 기반 for 루프: 컨테이너의 순회를 간결하게.
auto
사용: 복잡한 타입 선언을 줄이고 가독성 향상.
Unreal Engine 예제 코드
TArray<int32> MyArray = {1, 2, 3, 4};
for (auto Elem : MyArray) {
UE_LOG(LogTemp, Warning, TEXT("Element: %d"), Elem);
}
TUniquePtr<MyClass> MyUniquePtr = MakeUnique<MyClass>();
- Unreal Engine에서는 모던 C++의 스타일 가이드를 따르면서
TUniquePtr
, 범위 기반 for 루프 등을 활용하여 코드를 간결하고 안전하게 유지합니다.
'Unreal > GPT with Unreal' 카테고리의 다른 글
5.디자인패턴 (1) | 2024.12.03 |
---|---|
3.언리얼 멀티 쓰레드 (1) | 2024.11.30 |
2.블루프린트 (1) | 2024.11.29 |
1.UHT - UClass - CDO (2) | 2024.11.28 |
0.현대 게임 개발의 기본적인 모토 (2) | 2024.11.28 |