이미 다른 예외를 처리하면서 스택을 되돌리는 중에 소멸자가 호출될 수 있다. << 이게 핵심
예를들어 함수 안에서 예외가 발생하면 C++은 catch를 찾으면서 스택에 있던 지역 객체들을 정리함, 이 과정을 stack unwinding이라고 함
void Foo(){
SomeObject obj;
throw std::runtime_error("error");
}
여기서 예외가 발생하면 Foo()를 빠져나가기 전에 obj의 소멸자가 호출된다.
근데 그 소멸자가 또 예외를 밖으로 던지게 되면
첫번째 예외처리중
stack unwinding
obj의 소멸자 호출
소멸자에서 두 번째 예외 발생
std::terminate()
이미 예외하나가 전파되는 중인데 소멸자에서 또다른 예외가 밖으로 전파되는 상황이 위험한것
C++11 이후에는 일반적으로 소멸자가 기본적으로 noexcept 취급을 받는다.
Object Channel : 이 물체가 무엇인지를 분류 (Pawn, Character, WorldDynamic)
Trace Channel : 이 트레이스가 어떤 종류의 검사인지 분류(Visibility, Camera)
대상 오브젝트는 Trace Channel에 대해 어떻게 반응할지 설정함(Block / Overlap / Ignore)
AMyPlayerState* MyPS = Cast<AMyPlayerState>(PlayerState);
-> 어떤 타입으로 캐스팅할지
(PlayerState) -> 캐스팅할 실제 UObject 포인터
NewObject()랑 다름 여기는 T는 상속받는 타입이고 ()는 Outer잖아 구분잘하자
GameMode는 서버 전용으로 게임의 규칙과 진행 로직을 담당하고, GameState는 모든 플레이어가 알아야 하는 공용 상태를 담아 서버에서 클라이언트로 복제한다.
포워드 렌더링은 물체를 그릴 때 그 물체에 영향을 주는 조명을 같이 계산하는 방식이고, 디퍼드 렌더링은 먼저 화면에 보이는 표면의 색상, 노멀, 깊이 등의 정보를 G-Buffer에 저장한 뒤 별도 패스에서 조명을 계산하는 방식, 많은 동적 조명을 처리하기 유리하지만 G-Buffer 때문에 메모리와 대역폭을 많이 사용하고 투명 물체 처리가 어렵다는 단점이 있다.
프로세스는 독립적인 가상 주소 공간을 가지는 실행중인 프로그램, 프로세스끼리는 기본적으로 서로의 힙이나 전역변수에 직접 접근하지 못함
스레드는 그 프로세스 안에서 실제 코드를 실행하는 작업 단위, 같은 프로세스에 속한 스레드끼리는 같은 메모리 공간을 쓰기때문에 코드 영역, 전역/정적 데이터 영역, 힙 영역을 공유함. 하지만 각 스레드는 서로 다른 함수를 실행할 수 있으므로 스택, 레지스터값, 프로그램 카운터 같은 실행상태는 따로 가짐
컨텍스트 스위칭할때 같은 프로세스의 다른 스레드로 바뀌는 경우에는 주로 현재 스레드의 레지스터, 스택 포인터, 프로그램 카운터 같은 실행 상태를 저장하고 다음 스레드의 상태를 복원하면 된다.
반면에 다른 프로세스에 속한 스레드로 전환하면 실행 상태뿐 아니라 주소 공간 자체도 바뀌어서 Thread상태 교체, 페이지 테이블 기준 변경, TLB에 영향 때문에 일반적으로 비용이 더 크다.
여기서 실행중이던 스레드 정보 이런것들은 TCB라고 해서 커널 자료구조인 스레드 제어블록에 저장된다.
당연히 커널이기 때문에 운영체제가 관리
데이터 레이스는 여러 스레드가 같은 메모리에 동시 접근하고 하나이상이 쓰기 작업이며 적절한 동기화가 없을때 C++에서는 정의되지 않은 동작이 터질 수 있음 mutex, atomic등으로 공유 메모리를 안전하게 만듬
레이스 컨디션은 스레드들의 실행 순서나 타이밍에 따라 결과가 달라지는 더 넓은 문제, 단순히 순서만 정하는것이 아니라, 문제가 되는 연산 전체를 하나의 원자적인 흐름으로 보호하거나 상태 전이를 동기화해서 해결
UObject는 언리얼 오브젝트 시스템의 기본 클래스, 데이터 객체/에셋/ 여러 시스템 객체의 기반으로 사용, 기본적으로 월드에 배치되거나 트랜스폼을 가지는 객체는 아님
AActor는 UObject를 상속하고 월드에 배치되거나 Spawn될 수 있는 게임 오브젝트, 루트 컴포넌트를 통해 트랜스폼을 가질 수 있고, Beginplay나 Tick 네트워크 복제같은 월드 기능을 사용할 수 있다.
Outer는 이 UObject는 누구 소속인가? 누구 밑에 속한 객체로 취급할지
UPROPERTY/TObjectPtr 강한 참조 = 이 UObject를 아직 사용하고 있으니 GC가 추적해야 한다.
우선 end()는 이터레이터 반복자를 원소 끝 바로전에 접근하는것
sort를 못쓰는 이유는 sort는 랜덤접근을 해야하는데 list는 인덱스를 이용한 랜덤 접근이 불가능하다.
list.sort()로 정렬해야됨
rbegin/rend : 역순순회용 이터
cbegin/cend : const를 먹여서 이터레이터로 원소를 읽을수는 있지만 수정은 불가하게 함
같은키가 들어갈 수 있을까? insert로 넣으면 값이 안들어 갈수 있음, 그냥 단순 map[key] = newValue 이렇게 넣으면 newValue로 갱신
키 찾을때도 find말고 map[key] 이렇게 찾으면 map[key] = 0 으로 값이 들어가게됨 되도록이면 find로 찾자
lower_bound(x) : x 이상인 첫번째 원소
upper_bound(x) : x 이하인 첫번쨰 원소
set도 내부적으로 레드 블랙트리 기반이고 기본적으로 정렬된 상태유지, 값만 저장하고 값 정렬
그래서 삽입/삭제/탐색이 O(log N) find, count다 map과 똑같이 동작
그냥 중복 되고 안되고 차이, 솔직히 잘안씀, 내부적으로 얘네도 레드블랙트리인거만 알고있자
기본적으로 최대힙이라서 최대값이 먼저나옴
priority_queue<int, vector<int>, greater<int>> pq;
최소힙은 이렇게 greater붙여서 구현
누적합 구하는 함수
#include <numeric>
vector<int> v = {1, 2, 3, 4};
vector<int> psum(4);
partial_sum(v.begin(), v.end(), psum.begin());
// psum = {1, 3, 6, 10};
값을 하나 잡고 앞쪽이 정렬되어있다고 생각하고 비교한다. 뒤에서부터 비교하면서 더 큰 값들을 한칸씩 오른쪽으로 옮기면서 정렬, 정렬이 거의 다되어있을때 빠르다. 평균 N제곱이고 최선은 O(N)
체이닝, 서로 다른 키들이 같은 해시 인덱스로 충돌했을때 그 벜시안에 여러 원소들을 연결해서 저장하는 방식
개방 주소법은 다른 빈 버킷을 찾아서 이동하는 방식