💡 스마트 포인터란?

스마트 포인터(Smart Pointer)는 동적 메모리(new로 생성한 메모리`)의 생명주기 관리를 자동으로 해주는 클래스입니다.
일반 포인터처럼 사용할 수 있지만, 직접 delete 해줄 필요가 없습니다.

🎯 목적: 소유권 개념과 참조 횟수를 통해 메모리 누수댕글링 포인터(Dangling Pointer) 문제를 방지


🧩 스마트 포인터의 3종류

1. `unique_ptr<T>

  • 단 하나만 해당 객체를 소유할 수 있음 (복사 불가).
  • move()로만 소유권 이전 가능.
unique_ptr<int> ptr1 = make_unique<int>(10);
unique_ptr<int> ptr2(new int(10)); // 가능하지만 권장하지 않음

✔ 가볍고 빠름. 가장 안전한 기본 스마트 포인터
❌ 복사가 안 되기 때문에 container 등에 사용이 제한적


2. shared_ptr<T>

  • 여러 shared_ptr이 같은 객체를 공유할 수 있음.
  • 참조 횟수(Use Count)가 0이 될 때 메모리 해제
shared_ptr<int> ptr1 = make_shared<int>(10);
shared_ptr<int> ptr2 = ptr1; // 참조 카운트 +1

💡 make_shared는 객체와 ControlBlock을 한 번에 메모리 할당해 효율적이다.


3. weak_ptr<T>

  • shared_ptr비소유 참조.
  • 참조 카운트를 증가시키지 않음.
  • 순환 참조(Cyclic Reference) 해결용으로 사용됨.
shared_ptr<int> ptr1 = make_shared<int>(10);
weak_ptr<int> ptr2 = ptr1;

if (!ptr2.expired()) {
    shared_ptr<int> ptr3 = ptr2.lock(); // shared_ptr로 변환 후 사용
}

✔ 객체가 삭제됐는지 확인 가능
✔ 메모리 누수 해결용 (특히 Parent <-> Child 관계)


📦 shared_ptr 메모리 내부 구조

shared_ptr<int> ptr = make_shared<int>(10);

메모리 내부 구성은 다음과 같습니다:

[T* | RefCountBlock]
  • T*: 실제 객체 포인터
  • RefCountBlock: 참조 수 관리 구조체
    • _Uses: shared_ptr 참조 수
    • _Weaks: weak_ptr 참조 수

🔁 순환 참조 문제

class Parent {
    shared_ptr<Child> child;
};
class Child {
    shared_ptr<Parent> parent; // ⚠ 순환 참조!
};

→ 둘 다 shared_ptr이면 참조 카운트가 0이 안 되어 메모리 해제되지 않음!

✅ 해결법

class Child {
    weak_ptr<Parent> parent; // ✅ 순환 참조 해결
};

🔍 소멸자 내부 동작 분석

~shared_ptr() { this->_Decref(); }
  • _Decref()_Uses를 감소시키고 0이면 _Destroy() 호출
  • 이후 _Decwref()를 통해 _Weaks까지 0일 때 ControlBlock까지 해제
void _Decwref() {
    if (_Weaks == 0)
        _Delete_this(); // 완전한 메모리 해제
}

🧪 실습: 순환 참조 테스트

shared_ptr<Knight> k1(new Knight());
shared_ptr<Knight> k2(new Knight());

k1->SetTarget(k2);
k2->SetTarget(k1);

k1 = nullptr;
k2 = nullptr;

👆 SetTarget()에서 서로를 shared_ptr로 참조하면 객체가 해제되지 않음!
→ 둘 중 하나를 weak_ptr로 변경해야 안전하게 소멸 가능!


⚠ 스마트 포인터 사용 시 주의사항

주의점설명
shared_ptr끼리 순환 참조 ❌weak_ptr로 해결
make_shared 적극 사용메모리 할당 1회, 성능상 유리
unique_ptr은 소유권 이전만 가능복사 불가, move() 필수
shared_ptrweak_ptr 변환은 자동으로 참조 X반드시 lock()으로 사용해야 함
weak_ptr은 expired 상태 확인 필수접근 전에 유효성 체크!

profile
李家네_공부방

0개의 댓글