스마트 포인터(Smart Pointer)는 동적 메모리(new로 생성한 메모리`)의 생명주기 관리를 자동으로 해주는 클래스입니다.
일반 포인터처럼 사용할 수 있지만, 직접 delete 해줄 필요가 없습니다.
🎯 목적: 소유권 개념과 참조 횟수를 통해 메모리 누수와 댕글링 포인터(Dangling Pointer) 문제를 방지
<T>move()로만 소유권 이전 가능.unique_ptr<int> ptr1 = make_unique<int>(10);
unique_ptr<int> ptr2(new int(10)); // 가능하지만 권장하지 않음
✔ 가볍고 빠름. 가장 안전한 기본 스마트 포인터
❌ 복사가 안 되기 때문에 container 등에 사용이 제한적
shared_ptr<T>shared_ptr<int> ptr1 = make_shared<int>(10);
shared_ptr<int> ptr2 = ptr1; // 참조 카운트 +1
💡
make_shared는 객체와 ControlBlock을 한 번에 메모리 할당해 효율적이다.
weak_ptr<T>shared_ptr을 비소유 참조.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<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_ptr → weak_ptr 변환은 자동으로 참조 X | 반드시 lock()으로 사용해야 함 |
weak_ptr은 expired 상태 확인 필수 | 접근 전에 유효성 체크! |