포슀트

CS β€” runtime

CS β€” runtime

πŸ“— 04/15 - Runtime (λŸ°νƒ€μž„) 정리

Notion 원본: https://www.notion.so/343f77b24d2f8146b9dfcf1c60ebd0cd λΆ€λͺ¨: 자료 β†’ Cs λ©΄μ ‘μ€€λΉ„ β†’ 꼬리질문 ν‚€μ›Œλ“œ β†’ Runtime

λͺ¨μ˜λ©΄μ ‘ λ‹΅λ³€ β€” β€œλŸ°νƒ€μž„μ΄λž€ λ¬΄μ—‡μΈκ°€μš”?”

λŸ°νƒ€μž„(Runtime)은 ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰λ˜κ³  μžˆλŠ” μ‹œκ°„μ  단계λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. 컴파일 νƒ€μž„μ΄ μ†ŒμŠ€ μ½”λ“œλ₯Ό κΈ°κ³„μ–΄λ‘œ λ³€ν™˜ν•˜λŠ” 단계라면, λŸ°νƒ€μž„μ€ κ·Έ 결과물이 μ‹€μ œλ‘œ OS μœ„μ—μ„œ λ™μž‘ν•˜λŠ” λ‹¨κ³„μž…λ‹ˆλ‹€. λŸ°νƒ€μž„μ—λŠ” 동적 λ©”λͺ¨λ¦¬ ν• λ‹Ήκ³Ό ν•΄μ œ, 가상 ν•¨μˆ˜λ₯Ό ν†΅ν•œ 동적 λ””μŠ€νŒ¨μΉ˜, RTTIλ₯Ό ν™œμš©ν•œ νƒ€μž… νŒλ³„ 등이 μΌμ–΄λ‚©λ‹ˆλ‹€. λŸ°νƒ€μž„ μ—λŸ¬λŠ” μ»΄νŒŒμΌμ€ ν†΅κ³Όν•˜μ§€λ§Œ μ‹€ν–‰ 쀑에 λ°œμƒν•˜λŠ” 였λ₯˜λ‘œ, μ„Έκ·Έλ©˜ν…Œμ΄μ…˜ 폴트, μŠ€νƒ μ˜€λ²„ν”Œλ‘œμš°, μ •μ˜λ˜μ§€ μ•Šμ€ λ™μž‘(UB) 등이 λŒ€ν‘œμ μž…λ‹ˆλ‹€. 언리얼 μ—”μ§„μ—μ„œλ„ BeginPlay(), Tick() 같은 가상 ν•¨μˆ˜λŠ” λŸ°νƒ€μž„μ— vtable을 톡해 동적 λ””μŠ€νŒ¨μΉ˜λ˜λ©°, IsA()λ‚˜ Cast()λŠ” RTTI 기반으둜 λŸ°νƒ€μž„ νƒ€μž… 검사λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€.

ν‚€μ›Œλ“œ 정리

κΈ°λ³Έ κ°œλ…

  • λŸ°νƒ€μž„ (Runtime) β€” ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰ 쀑인 단계; 컴파일 νƒ€μž„ 이후 OS μœ„μ—μ„œ λ™μž‘ν•˜λŠ” μ‹œκ°„
  • 컴파일 νƒ€μž„ (Compile Time) β€” μ†ŒμŠ€ μ½”λ“œλ₯Ό κΈ°κ³„μ–΄λ‘œ λ³€ν™˜ν•˜λŠ” 단계; νƒ€μž… κ²€μ‚¬Β·μ΅œμ ν™” λ°œμƒ
  • 동적 λ©”λͺ¨λ¦¬ ν• λ‹Ή (Dynamic Memory Allocation) β€” λŸ°νƒ€μž„μ— νž™μ—μ„œ λ©”λͺ¨λ¦¬λ₯Ό μš”μ²­ (new / malloc)
  • RTTI (Runtime Type Information) β€” λŸ°νƒ€μž„μ— 객체의 μ‹€μ œ νƒ€μž…μ„ μ‘°νšŒν•˜λŠ” C++ λ©”μ»€λ‹ˆμ¦˜
  • 동적 λ””μŠ€νŒ¨μΉ˜ (Dynamic Dispatch) β€” vtable을 톡해 λŸ°νƒ€μž„μ— μ‹€μ œ ν•¨μˆ˜λ₯Ό κ²°μ •Β·ν˜ΈμΆœ
  • μ •μ˜λ˜μ§€ μ•Šμ€ λ™μž‘ (Undefined Behavior, UB) β€” ν‘œμ€€μ΄ κ²°κ³Όλ₯Ό κ·œμ •ν•˜μ§€ μ•ŠλŠ” μ½”λ“œ; λŸ°νƒ€μž„μ— 예츑 λΆˆκ°€

ν‚€μ›Œλ“œ λΆ„λ₯˜ν‘œ

  • νƒ€μž… 검사 β†’ 컴파일 νƒ€μž„ (static_assert, ν…œν”Œλ¦Ώ μΈμŠ€ν„΄μŠ€ν™”)
  • 동적 λ””μŠ€νŒ¨μΉ˜ β†’ λŸ°νƒ€μž„ (virtual ν•¨μˆ˜ 호좜, vtable μ°Έμ‘°)
  • RTTI β†’ λŸ°νƒ€μž„ (dynamic_cast, typeid)
  • λ©”λͺ¨λ¦¬ ν• λ‹Ή β†’ λŸ°νƒ€μž„ (new/delete, malloc/free)
  • λŸ°νƒ€μž„ μ—λŸ¬ β†’ λŸ°νƒ€μž„ (segfault, μŠ€νƒ μ˜€λ²„ν”Œλ‘œμš°, UB)

컴파일 νƒ€μž„ vs λŸ°νƒ€μž„

컴파일 νƒ€μž„μ€ μ†ŒμŠ€ μ½”λ“œλ₯Ό κΈ°κ³„μ–΄λ‘œ λ²ˆμ—­ν•˜λŠ” λ‹¨κ³„λ‘œ, 문법 κ²€μ‚¬Β·νƒ€μž… κ²€μ‚¬Β·μ΅œμ ν™”κ°€ μΌμ–΄λ‚©λ‹ˆλ‹€. λŸ°νƒ€μž„μ€ 컴파일된 ν”„λ‘œκ·Έλž¨μ΄ OS μœ„μ—μ„œ μ‹€μ œλ‘œ μ‹€ν–‰λ˜λŠ” λ‹¨κ³„λ‘œ, λ©”λͺ¨λ¦¬ 할당·동적 λ””μŠ€νŒ¨μΉ˜Β·I/O 등이 λ°œμƒν•©λ‹ˆλ‹€.

비ꡐ:

  • μ‹œμ : λΉŒλ“œ μ‹œ / μ‹€ν–‰ μ‹œ
  • μ£Όμš” μž‘μ—…: λ¬Έλ²•Β·νƒ€μž… 검사, μ΅œμ ν™”, μ½”λ“œ 생성 / λ©”λͺ¨λ¦¬ ν• λ‹Ή, 동적 λ””μŠ€νŒ¨μΉ˜, I/O
  • μ—λŸ¬ 발견: 컴파일 μ—λŸ¬ (μ¦‰μ‹œ ν”Όλ“œλ°±) / λŸ°νƒ€μž„ μ—λŸ¬ (μ‹€ν–‰ 쀑 ν¬λž˜μ‹œ)
  • μ„±λŠ₯ λΉ„μš©: λΉŒλ“œ μ‹œκ°„μ— μ†Œλͺ¨ / μ‹€ν–‰ 쀑 μ†Œλͺ¨ (직접 영ν–₯)
1
2
3
4
5
6
7
8
9
10
11
12
// ── 컴파일 νƒ€μž„ ──────────────────────────────────────────
static_assert(sizeof(int) == 4, "intλŠ” 4λ°”μ΄νŠΈμ—¬μ•Ό 함");
constexpr int Square(int x) { return x * x; }
template<typename T> void Print(T val) { }

// ── λŸ°νƒ€μž„ ───────────────────────────────────────────────
int n;
std::cin >> n;                    // λŸ°νƒ€μž„μ— κ°’ κ²°μ •
int* arr = new int[n];            // λŸ°νƒ€μž„ λ©”λͺ¨λ¦¬ ν• λ‹Ή
Base* p = new Derived();          // λŸ°νƒ€μž„ 객체 생성
p->VirtualFunc();                 // λŸ°νƒ€μž„ 동적 λ””μŠ€νŒ¨μΉ˜
delete[] arr;                     // λŸ°νƒ€μž„ λ©”λͺ¨λ¦¬ ν•΄μ œ

볡기:

  • 컴파일 νƒ€μž„ λΉ„μš© = λΉŒλ“œ μ‹œκ°„ 증가 (μ‹€ν–‰ μ„±λŠ₯에 무관)
  • λŸ°νƒ€μž„ λΉ„μš© = μ‹€μ œ μ‹€ν–‰ 속도에 직접 영ν–₯
  • constexpr / ν…œν”Œλ¦Ώ / static_assert β†’ 컴파일 νƒ€μž„μœΌλ‘œ λΉ„μš© 이동
  • virtual / new / dynamic_cast β†’ λŸ°νƒ€μž„ λΉ„μš© λ°œμƒ

RTTI (Runtime Type Information)

RTTIλŠ” λŸ°νƒ€μž„μ— 객체의 μ‹€μ œ(동적) νƒ€μž…μ„ μ‘°νšŒν•  수 있게 ν•΄μ£ΌλŠ” C++ λ©”μ»€λ‹ˆμ¦˜μž…λ‹ˆλ‹€. dynamic_cast와 typeid 두 κ°€μ§€ λ°©λ²•μœΌλ‘œ μ‚¬μš©ν•˜λ©°, 가상 ν•¨μˆ˜κ°€ μžˆλŠ” 클래슀(vtable 보유)μ—μ„œλ§Œ λ™μž‘ν•©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal { public: virtual ~Animal() {} };
class Dog    : public Animal { public: void Bark() {} };
class Cat    : public Animal {};

Animal* a = new Dog();

// 1. dynamic_cast β€” μ•ˆμ „ν•œ λ‹€μš΄μΊμŠ€νŠΈ
Dog* d = dynamic_cast<Dog*>(a);  // 성곡
if (d) d->Bark();

Cat* c = dynamic_cast<Cat*>(a);  // μ‹€νŒ¨: nullptr λ°˜ν™˜

// 2. typeid β€” νƒ€μž… 정보 쑰회
#include <typeinfo>
std::cout << typeid(*a).name();
if (typeid(*a) == typeid(Dog)) { /* Dogμž„μ„ 확인 */ }

dynamic_cast vs static_cast:

  • 검사 μ‹œμ : 컴파일 νƒ€μž„ / λŸ°νƒ€μž„
  • μ•ˆμ „μ„±: ν”„λ‘œκ·Έλž˜λ¨Έ μ±…μž„ / μ‹€νŒ¨ μ‹œ nullptr (포인터) / μ˜ˆμ™Έ (μ°Έμ‘°)
  • μ„±λŠ₯: μ˜€λ²„ν—€λ“œ μ—†μŒ / vtable 참쑰둜 λŸ°νƒ€μž„ λΉ„μš© λ°œμƒ
  • μ‚¬μš© 쑰건: 가상 ν•¨μˆ˜ λΆˆν•„μš” / 가상 ν•¨μˆ˜ 1개 이상 ν•„μš”

볡기:

  • RTTI = dynamic_cast + typeid β€” 가상 ν•¨μˆ˜ μžˆλŠ” ν΄λž˜μŠ€μ—μ„œλ§Œ λ™μž‘
  • dynamic_cast μ‹€νŒ¨ μ‹œ 포인터면 nullptr, μ°Έμ‘°λ©΄ std::bad_cast μ˜ˆμ™Έ
  • -fno-rtti 컴파일 μ˜΅μ…˜μœΌλ‘œ RTTI λΉ„ν™œμ„±ν™” κ°€λŠ₯
  • RTTI λ‚¨μš©μ€ 섀계 문제 μ‹ ν˜Έ β€” 가상 ν•¨μˆ˜λ‘œ λŒ€μ²΄ κ³ λ €

λŸ°νƒ€μž„ μ—λŸ¬ μœ ν˜•

λŸ°νƒ€μž„ μ—λŸ¬λŠ” μ»΄νŒŒμΌμ€ μ„±κ³΅ν•˜μ§€λ§Œ μ‹€ν–‰ 쀑에 ν”„λ‘œκ·Έλž¨μ΄ 비정상 μ’…λ£Œλ˜κ±°λ‚˜ 잘λͺ»λœ κ²°κ³Όλ₯Ό λ‚΄λŠ” 였λ₯˜μž…λ‹ˆλ‹€.

μ£Όμš” μœ ν˜•:

  • Segmentation Fault β€” 잘λͺ»λœ λ©”λͺ¨λ¦¬ μ ‘κ·Ό (널 포인터 μ—­μ°Έμ‘°, ν•΄μ œλœ λ©”λͺ¨λ¦¬ μ ‘κ·Ό)
  • Stack Overflow β€” μŠ€νƒ λ©”λͺ¨λ¦¬ 초과 (λ¬΄ν•œ μž¬κ·€, κ³Όλ„ν•œ μ§€μ—­ λ³€μˆ˜)
  • Undefined Behavior (UB) β€” ν‘œμ€€ λ―Έμ •μ˜ λ™μž‘ (λΆ€ν˜Έ μžˆλŠ” μ •μˆ˜ μ˜€λ²„ν”Œλ‘œμš°, λ²”μœ„ 초과 μ ‘κ·Ό)
  • λ©”λͺ¨λ¦¬ λˆ„μˆ˜ (Memory Leak) β€” ν• λ‹Ή ν›„ ν•΄μ œ λˆ„λ½ (new ν›„ delete μ—†μŒ)
  • Double Free β€” 같은 λ©”λͺ¨λ¦¬ 두 번 ν•΄μ œ
  • std::exception β€” out_of_range, bad_alloc, bad_cast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int* p = nullptr;
*p = 42;               // nullptr μ—­μ°Έμ‘° β†’ segfault

void Infinite() { Infinite(); }   // λ¬΄ν•œ μž¬κ·€ β†’ μŠ€νƒ μ˜€λ²„ν”Œλ‘œμš°

void Leak() {
    int* x = new int(42);
    // delete x; μ—†μŒ β†’ λ©”λͺ¨λ¦¬ λˆ„μˆ˜
}

// μ•ˆμ „ν•œ νŒ¨ν„΄: RAII / 슀마트 포인터
void Safe() {
    auto x = std::make_unique<int>(42);
}

볡기:

  • λŸ°νƒ€μž„ μ—λŸ¬λŠ” μ»΄νŒŒμΌλŸ¬κ°€ μž‘μ§€ λͺ»ν•¨ β†’ μ‹€ν–‰ 쀑 ν¬λž˜μ‹œ
  • UBλŠ” ν¬λž˜μ‹œ 없이 쑰용히 잘λͺ»λœ κ²°κ³Όλ₯Ό λ‚Ό 수 μžˆμ–΄ κ°€μž₯ μœ„ν—˜
  • RAII / 슀마트 ν¬μΈν„°λ‘œ λ©”λͺ¨λ¦¬ λˆ„μˆ˜Β·double free λ°©μ§€
  • AddressSanitizer, Valgrind둜 λ©”λͺ¨λ¦¬ 였λ₯˜ 탐지

동적 λ©”λͺ¨λ¦¬ ν• λ‹Ή (νž™ vs μŠ€νƒ)

μŠ€νƒμ€ ν•¨μˆ˜ 호좜 μ‹œ μžλ™μœΌλ‘œ ν• λ‹ΉΒ·ν•΄μ œλ˜λŠ” λ©”λͺ¨λ¦¬ μ˜μ—­μ΄κ³ , νž™μ€ λŸ°νƒ€μž„μ— ν”„λ‘œκ·Έλž˜λ¨Έκ°€ 직접 new/delete둜 κ΄€λ¦¬ν•˜λŠ” λ©”λͺ¨λ¦¬ μ˜μ—­μž…λ‹ˆλ‹€.

νž™ vs μŠ€νƒ:

  • ν• λ‹Ή μ‹œμ : ν•¨μˆ˜ μ§„μž… μ‹œ μžλ™ / λŸ°νƒ€μž„ new/malloc 호좜 μ‹œ
  • ν•΄μ œ μ‹œμ : ν•¨μˆ˜ μ’…λ£Œ μ‹œ μžλ™ / delete/free 호좜 μ‹œ (μˆ˜λ™)
  • 크기: μ œν•œμ (1~8MB) / κ°€μš© RAM ν•œλ„ λ‚΄
  • 속도: 빠름 (SP λ ˆμ§€μŠ€ν„° 이동) / 느림 (OS μ‹œμŠ€ν…œ 콜 κ°€λŠ₯)
  • 수λͺ…: μŠ€μ½”ν”„ λ‚΄ / λͺ…μ‹œμ  ν•΄μ œ μ „κΉŒμ§€
1
2
3
4
5
6
7
void Example() {
    int  stackVar = 10;          // μŠ€νƒ β€” μžλ™ ν•΄μ œ
    int* heapVar  = new int(20); // νž™ β€” μˆ˜λ™ ν•΄μ œ ν•„μš”

    auto smart = std::make_unique<int>(30);  // RAII
    delete heapVar;
}

언리얼 μ—”μ§„κ³Ό λŸ°νƒ€μž„

언리얼 엔진은 λŸ°νƒ€μž„μ— λ‹€μ–‘ν•œ λ©”μ»€λ‹ˆμ¦˜μ„ μ‚¬μš©ν•©λ‹ˆλ‹€. Cast<T>()λŠ” RTTI 기반 μ•ˆμ „ν•œ λ‹€μš΄μΊμŠ€νŠΈ, IsA<T>()λŠ” νƒ€μž… 검사, κ°€λΉ„μ§€ 컬렉터(GC)κ°€ UObject νž™ λ©”λͺ¨λ¦¬λ₯Ό λŸ°νƒ€μž„μ— μžλ™ κ΄€λ¦¬ν•©λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯:

  • Cast<T>() β€” UObject νƒ€μž… μ•ˆμ „ λ‹€μš΄μΊμŠ€νŠΈ (dynamic_cast μœ μ‚¬)
  • IsA<T>() β€” λŸ°νƒ€μž„ νƒ€μž… 검사 (typeid μœ μ‚¬)
  • κ°€λΉ„μ§€ 컬렉터 (GC) β€” UObject λ©”λͺ¨λ¦¬ μžλ™ 회수 (delete μžλ™ν™”)
  • BeginPlay() / Tick() β€” vtable 동적 λ””μŠ€νŒ¨μΉ˜ (virtual ν•¨μˆ˜ 호좜)
  • SpawnActor<T>() β€” λŸ°νƒ€μž„ 객체 생성
1
2
3
4
5
6
7
8
9
10
AActor* Actor = GetOwner();
ACharacter* Char = Cast<ACharacter>(Actor);  // μ‹€νŒ¨ μ‹œ nullptr
if (Char) Char->Jump();

if (Actor->IsA<ACharacter>()) { /* ACharacter νŒŒμƒ νƒ€μž… */ }

UMyObject* Obj = NewObject<UMyObject>(this);  // GCκ°€ λŸ°νƒ€μž„ 관리
// delete Obj;  // ← μ ˆλŒ€ κΈˆμ§€! GCκ°€ μžλ™ ν•΄μ œ

AMyActor* Spawned = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass());

볡기:

  • Cast<T>() = 언리얼 방식 dynamic_cast; μ‹€νŒ¨ μ‹œ nullptr
  • UObjectλŠ” GCκ°€ λ©”λͺ¨λ¦¬ 관리 β†’ 직접 delete κΈˆμ§€
  • UPROPERTY() λ§€ν¬λ‘œκ°€ μžˆμ–΄μ•Ό GCκ°€ 레퍼런슀 좔적 κ°€λŠ₯
  • λŸ°νƒ€μž„ μ„±λŠ₯ 민감 μ½”λ“œ(Tick)μ—μ„œ Cast λ‚¨μš© 주의 β€” 캐싱 ꢌμž₯
이 κΈ°μ‚¬λŠ” μ €μž‘κΆŒμžμ˜ CC BY 4.0 λΌμ΄μ„ΌμŠ€λ₯Ό λ”°λ¦…λ‹ˆλ‹€.