2026-03-05 TIL (8일차)
C++ 강의 (2주차)
스마트 포인터
원리: 스마트 원리의 핵심 원리는 new / delete를 사용하지 않는 자동 메모리 관리unique_ptr
● 힙 메모리에 대해 독점적 소유권을 가지는 스마트 포인터 (주소 하나에 포인터 하나)
● 소유권 이전은 가능하지만 동시에 가리키는거는 불가shared_ptr
● 하나의 자원(힙 메모리)을 여러 포인터가 공동 소유하며, 참조 횟수(Reference Count)가 0이 될 때 자동으로 메모리를 해제하는 포인터
● 참초 횟수: 소유하고 있는 포인터 갯수
● 문제점: 순환참조를 하게되면 해제가 불가능 하다. (힙 메모리에 내용을 파괴할 때 자신을 가리키고 있는 포인터가 아직 남아있으므로 계속 남아있게 된다.)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
class Cat { public: Cat(std::string name) : mName{ name } { std::cout << mName << " cat constructor" << std::endl; } ~Cat() { std::cout << mName << " cat destructor" << std::endl; } std::shared_ptr<Cat> mVar; private: std::string mName; }; int main() { std::shared_ptr<Cat> kitty = std::make_shared<Cat>("kitty"); std::shared_ptr<Cat> nabi = std::make_shared<Cat>("nabi"); kitty->mVar = nabi; nabi->mVar = kitty; std::cout << "kitty count : " << kitty.use_count() << std::endl; std::cout << "nabi count : " << nabi.use_count() << std::endl; }
weak_ptr (shared_ptr과 연관이 있음)
● 객체를 가리키기는 하지만, 소유권(참조 횟수)을 주장하지 않는 지켜보기 전용 포인터
● shared_ptr을 가리키고 있는 주소값을 똑같이 weak_ptr이 가리키게 해도 참조 횟수가 증가하지 않는다.
● shared_ptr의 문제점인 순환참조가 안일어나게 할 수 있다.
● weak_ptr에서 lock()은 객체가 살아있는지 확인하고, 살아있다면 내가 안전하게 사용할 수 있도록 ‘임시 소유권’을 빌려오는 기능이고 사용이 끝나면 빌려온 권한은 자동으로 사라지고, 객체는 다시 언제든 파괴될 수 있는 상태로 돌아간다.언리얼 엔진 메모리관리
가비지 컬렉션
더 이상 아무도 쓰지 않는 메모리(객체)를 엔진이 알아서 청소해 주는 시스템
루트셋(Root Set)에 제외한 모든 객체는 가비지 컬렉션에 들어감루트셋: 무조건 삭제시키지말고 놔둬야한다는 최상위 객체 목록
ex) Game Instance, World, Player Controller, Engine Subsystems 등언리얼 엔진 메모리 관리 객체 분류
구분 가비지컬렉션 수거 여부 판단 기준 예시 생존 객체 제외 루트셋 그 자체이거나, 루트셋으로부터 참조(UPROPERTY)이 연결되어 있음 루트셋, 참조된 객체 가비지 객체 대상 루트셋으로부터 연결된 참조가 없는 상태 Destroy된 액터, 참조를 잃은 UObject, 함수 안의 지역 변수, 생성만 하고 어디에도 등록 안 한 UI 마크 앤 스윕 알고리즘
첫번째 단계: 루트셋에서 포함된 객체 식별
두번째 단계: 루트셋 객체에서 직간접적으로 참조하는 객체를 판단하고 마크함
세번째 단계: 마크 단계가 완료되면 마크되지 않은 객체들이 차지하고 있던 메모리를 회수가비지 컬렉션은 단순히 지운다,안 지운다를 넘어, 각 객체의 상태를 플래그(Flag)라는 꼬리표로 관리
● RF_RootSet: 개발자가 직접 수동으로 관리하겠다.수동으로 지우기 전까지 건드리지 마라는 플래그● RF_BeginDestroyed(시스템이 내부적으로 관리하는 값)
파괴 로직을 막 시작한 단계, BeginDestroyed() 가상 함수가 호출되었음을 의미
이 상태의 객체는 아직 메모리에 데이터는 남아있지만, 더 이상 정상적인 기능을 수행할 수 없습니다.● RF_FinishDestroyed(시스템이 내부적으로 관리하는 상태 값)
모든 파괴 준비 작업이 끝난 단계, inishDestroyed() 가상 함수가 호출되었음을 의미
객체가 차지하던 공간을 비우기 직전의 마지막 단계, 이 플래그가 찍히면 더 이상 엔진의 어떤 시스템도 이 객체에 접근해서는 안 됨.리플렉션 시스템
엔진이 코드를 실시간으로 인식하고 에디터 노출·가비지 컬렉션·블루프린트와 연동할 수 있게 만드는 통역 시스템
(기본 c++로 하면 언리얼 엔진에서 못받아들이고 사용을 못함)
C++ 공부
상속받은 클래스의 생성자 정의
● 자식 클래스의 생성자는 부모 클래스의 멤버까지 초기화(생성자 호출)해야 할 의무가 있다.
● 자식은 부모의 구성까지 생성이 되므로 자식 클래스 생성자는, 부모 클래스의 생성자를 호출해서 부모 클래스의 멤버를 초기화 하는 것이 좋다.
자식 클래스 생성 과정
● 자식 클래스의 객체생성 과정에서 부모 클래스의 생성자는 100% (대부분 먼저)호출된다.
● 자식 클래스의 생성자에서 부모 클래스의 생성자 호출을 명시하지 않으면, 기초 클래스의 void 생성자가 호출된다.
자식 클래스 객체의 소멸과정
● 자식 클래스의 객체가 소멸될 때에는, 자식 클래스의 소멸자가 실행되고 난 다음에 부모 클래스의 소멸자가 실행된다.
● 스택에 생성된 객체의 소멸순서는 생성순서와 반대이다.
상속 형태의 3가지
| 접근 제한자 | 본인 클래스 | 자식 클래스 | 외부 |
|---|---|---|---|
| public | ✅ 가능 | ✅ 가능 | ✅ 가능 |
| protected | ✅ 가능 | ✅ 가능 | ❌ 불가 |
| private | ✅ 가능 | ❌ 불가 | ❌ 불가 |
Is-A와 Has-A차이
| 구분 | IS-A (상속) | HAS-A (구성) |
|---|---|---|
| 관계 | 클래스 간의 수직적 관계 | 객체 간의 수평적/포함 관계 |
| 결합도 | 강함 (부모가 바뀌면 자식도 바뀜) | 약함 (부품만 교체하면 됨) |
| 유연성 | 낮음 (설계 시점에 결정됨) | 높음 (실행 중에 바꿀 수 있음) |
| 코드 재사용 | 부모의 코드를 그대로 물려받음 | 필요한 기능을 가진 객체를 가져다 씀 |
1
2
3
4
5
6
class A
{
}
class B : public A
{
}
1
2
3
4
5
6
7
class A
{
}
class B
{
A* aptr
}