c++/알고리즘

C++ 알고리즘) 스마트 포인터 및 언리얼 가비지 컬렉션

eclipse2 2026. 3. 5. 20:59

메모리를 직접 관리하는 c++에서 dagling pointer가 발생하지 않도록 스마트 포인터를 제공한다.

  1. unique_ptr - 객체에 대한 단일 소유권을 관리 (두개의 포인터가 하나의 공간을 소유할 수 없음, move를 통해 소유권 이전은 가능)
  2. shared_ptr - 레퍼런스 카운트를 관리 (현재 객체를 참조하는 포인터의 개수를 카운팅하는 것)
  3. weak_ptr - 객체의 소유권을 공유하지 않음 (shared_ptr은 서로를 가리켜 메모리 누수를 유발할 수 있기에 둘중에 하나를 weak_ptr로 대체하면 순환 고리가 끊어지게 됨)

사용법

#include <iostream>
#include <memory> //스마트 포인터 사용
using namespace std;

int main() {
    // unique_ptr 생성
    unique_ptr<int> ptr1 = make_unique<int>(20);

    // 소유권 이동 (move 사용)
    unique_ptr<int> ptr2 = move(ptr1);

    if (!ptr1) {
        cout << "ptr1은 이제 비어 있습니다." << endl;
    }
    cout << "ptr2의 값: " << *ptr2 << endl;

    return 0;
}
#include <iostream>
#include <memory>
using namespace std;

class MyClass {
public:
    MyClass(int val) : value(val) {
        cout << "MyClass 생성: " << value << endl;
    }
    ~MyClass() {
        cout << "MyClass 소멸: " << value << endl;
    }
    void display() const {
        cout << "값: " << value << endl;
    }
private:
    int value;
};

int main() {
    // unique_ptr로 MyClass 객체 관리
    unique_ptr<MyClass> myObject = make_unique<MyClass>(42);

    // MyClass 멤버 함수 호출
    myObject->display();

    // 소유권 이동
    unique_ptr<MyClass> newOwner = move(myObject);

    if (!myObject) {
        cout << "myObject는 이제 비어 있습니다." << endl;
    }
    newOwner->display();

    // 범위를 벗어나면 newOwner가 관리하는 메모리 자동 해제
    return 0;
}
#include <iostream>
#include <memory> // shared_ptr 사용
using namespace std;

int main() {
    // shared_ptr 생성
    shared_ptr<int> ptr1 = make_shared<int>(10);

    // ptr1의 참조 카운트 출력
    cout << "ptr1의 참조 카운트: " << ptr1.use_count() << endl; // 출력: 1

    // ptr2가 ptr1과 리소스를 공유
    shared_ptr<int> ptr2 = ptr1;
    cout << "ptr2 생성 후 참조 카운트: " << ptr1.use_count() << endl; // 출력: 2

    // ptr2가 범위를 벗어나면 참조 카운트 감소
    ptr2.reset();
    cout << "ptr2 해제 후 참조 카운트: " << ptr1.use_count() << endl; // 출력: 1

    // 범위를 벗어나면 ptr1도 자동 해제
    return 0;
}
#include <iostream>
#include <memory>
using namespace std;

class MyClass {
public:
    MyClass(int val) : value(val) {
        cout << "MyClass 생성: " << value << endl; // 출력: MyClass 생성: 42
    }
    ~MyClass() {
        cout << "MyClass 소멸: " << value << endl; // 출력: MyClass 소멸: 42
    }
    void display() const {
        cout << "값: " << value << endl; // 출력: 값: 42
    }
private:
    int value;
};

int main() {
    // shared_ptr로 MyClass 객체 관리
    shared_ptr<MyClass> obj1 = make_shared<MyClass>(42);

    // 참조 공유
    shared_ptr<MyClass> obj2 = obj1;

    cout << "obj1과 obj2의 참조 카운트: " << obj1.use_count() << endl; // 출력: 2

    obj2->display(); // 출력: 값: 42

    // obj2를 해제해도 obj1이 객체를 유지
    obj2.reset();
    cout << "obj2 해제 후 obj1의 참조 카운트: " << obj1.use_count() << endl; // 출력: 1

    return 0;
}
#include <iostream>
#include <memory>
using namespace std;

class MyClass {
public:
    MyClass(int val) : value(val) {
        cout << "MyClass 생성: " << value << endl; // 출력: MyClass 생성: 42
    }
    ~MyClass() {
        cout << "MyClass 소멸: " << value << endl; // 출력: MyClass 소멸: 42
    }
    void display() const {
        cout << "값: " << value << endl; // 출력: 값: 42
    }
private:
    int value;
};

int main() {
    // shared_ptr로 MyClass 객체 관리
    shared_ptr<MyClass> obj1 = make_shared<MyClass>(42);

    // 참조 공유
    shared_ptr<MyClass> obj2 = obj1;

    cout << "obj1과 obj2의 참조 카운트: " << obj1.use_count() << endl; // 출력: 2

    obj2->display(); // 출력: 값: 42

    // obj2를 해제해도 obj1이 객체를 유지
    obj2.reset();
    cout << "obj2 해제 후 obj1의 참조 카운트: " << obj1.use_count() << endl; // 출력: 1

    return 0;
}

언리얼엔진의 메모리 관리

언리얼 엔진은 객체들의 메모리 관리를 자동화하기 위해 가비지 컬렉션 시스템을 사용 → 이는 마치 로봇 청소기가 사용하지 않는 물건(메모리)을 알아서 치워주는 것과 같다.

이를 통해 메모리 누수나 dangling pointer 와 같은 오류가 줄어든다.

가비지 컬렉션 - 마크 앤 스윕 알고리즘 방식으로 작동

주기적으로 사용하지 않는 UObject들을 식별하여 제거

  1. 루트셋에서 시작
    1. 루트셋에 포함된 객체들을 식별
    2. 항상 살아있다고 간주되는 특별 객체
    3. 엔진, 컨트롤러
  2. 마크 단계
    1. 사용중인 객체를 마크(표시)함
  3. 스윕 단계 - 메모리 회수
    1. 마크 되지 않은 객체들이 차지하고 있던 메모리를 회수 함

가비지컬렉션 동작 방식을 제어하는 플레그들

  1. RF_RootSet
    1. 이 플러그가 설정된 객체는 루트셋의 일부로 관리
    2. 즉, 가비지 컬렉션 대상이 아님. AddToRoot()으로 설정, RemoveFromRoot() 으로 헤제
  2. RF_BeginDestoryed
    1. 해당 함수 BeginDestroy() 는 객체가 실제로 메모리에서 해제되기 전에 필요한 정리 작업을 수행하는 함수
  3. RF_FinishDestoryed
    1. 해당 함수 FinishDestory() 호출 후 객체의 메모리가 완전히 해제

언리얼 엔진의 리플렉션 시스템

프로그램이 실행 중에 자신의 구조와 상태를 검사하고 수정할 수 있는 능력을 말한다.

리플렉션은 UObject를 위한 운영체제

사용자가 작성한 코드 → 언리얼에서는 이해할 수 없음 → 따라서 Unreal engine tool(UHT) 에서 매크로를 사용해서 설명

매크로

  1. UCLASS()
  2. UPROPERTY()
  3. UFUNCTION()
  4. USTRUCT()
  5. GENERATED_BODY()

사용자 정의 객체 → 리플렉션 → 언리얼 엔진 속성을 가진 객체 → 가비지 컬렉션, 에디터 통합, 비주얼 스크립팅, 블루 플린트.