TIL: 스마트 포인터

박춘팔·2026년 4월 21일

CPP TIL

목록 보기
11/15

📅 2026-04-21

오전 1시간동안 코드카타를 하는데 아직 그럴 실력이 안돼서 일단 문법 공부를 더 해보려고한다.

스마트 포인터

19일 작성한 RAII 패턴 기반의 포인터 래퍼이다.
사용하는 목적은 수동으로 해주던 new/delete의 할당/해제를 자동으로 해준다. 또한 소유권을 명확하게 하는데 의의가 있다.

왜 필요한가?

일반 포인터는 주소를 담고 있다는 사실을 알고있다.
하지만

  • 누가 해제해야하는지
  • 언제 소멸하는지
  • 소유권이 누구한테 있는지

이게 코드만 보고 명확하지 않다.
그래서 이중 해제, 누수, dangling pointer가 생긴다.
C++ 가이드라인에서 소유권이 필요하면 스마트 포인터, 소유권 자체가 필요없다면 일반 값이나 참조, 일반 포인터가 더 적절하다고 한다.

dangling pointer?

이미 해제된 메모리 영역을 가르키는 포인터를 말함

  • 포인터가 주소를 가지고있음
  • 근데 그 주소는 이미 해제됨
  • 정의되지 않은 동작 발생할 확률 증가

메모리 해제 후 포인터 유지

int* p = new int(10);

delete p; // p는 아직 주소 가지고 있음

지역 변수 주소 반환

function RtrnPointer()
{
	int x = 10;
	
    return &x; // 함수가 끝나면 지역변수 사라짐
}

객체 소멸 후 참조 유지

int* p;


function RtrnPointer()
{
	int x = 10;
    p = &x;w
} // x소멸

단일소유권 std::unique_ptr

std::unique_ptr<int> p = std::make_unique<int>(10);

하나의 객체만 특정 자원을 소유하는 구조.

  • 스코프를 벗어남.
  • reset()
  • 다른 포인터로 재대입
    위 3가지 경우에 관리 대상이 삭제된다.
    또한 커스텀 deleter를 둘 수 있고 move가능 copy 불가능하다.

move와 copy?

// copy 
std::unique_ptr<int> p1 = std::make_unique<int>(10);
std::unique_ptr<int> p2 = p1; // 컴파일 에러

// move
std::unique_ptr<int> p1 = std::make_unique<int>(10);
std::unique_ptr<int> p2 = std::move(p1);

공유소유권 std::shared_ptr

#include <memory>

std::shared_ptr<int> p1 = std::make_shared<int>(10);
std::shared_ptr<int> p2 = p1; // 공동소유

shared_ptr는 여러 포인터가 같은 객체를 공동 소유한다.
같은 객체를 소유한 여러 포인터 중 마지막 포인터가 소멸되거나 다른 대상으로 지정되면 객체가 삭제된다.

왜 필요한가?

  • 여러 객체/시스템이 실제로 같은 대상을 함께 소유해야할 경우
  • 소유자가 동적으로 늘어나거나 줄어들어서 하나로 정하기 애매할 때
  • 콜백, 비동기, 옵저버 패턴에서 수명 연장이 명시적으로 필요할 때

주의

자칫 소유권이 불명확한 설계를 덮어버리는 도구가 되기 쉽다.
공유 포인터는 런타임 코스트를 수반하므로 자제하는게 좋다.

감시 std::weak_ptr

#include <memory>


std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp;

if (auto locked = wp.lock())
{ ... }

weak_ptr은 shared_ptr이 관리하는 객체를 소유하지 않고 가리키기만 한다.

역참조가 불가능하며 lock()으로 임시로 shared_ptr을 얻어서 사용해야한다.
또한 expired()로 삭제 됐는지 확인이 가능하다.

왜 필요한가?

가장 대표적인 이유는 순환참조 방지이다.

struct B;

struct A
{
	std::shared_ptr<B> b;
}

struct B
{
	std::shared_ptr<A> a; // 서로 shared_ptr이면 해제 안됨
}

위의 구조는 서로가 서로를 소유해서 참조 카운트가 0이 안된다.
때문에 메모리 릭이 발생하는데 이렇게 설계해야할 경우 한쪽을 weak으로 끊으면 된다.

  • 부모-자식, 옵저버, 이벤트리스너처럼 연결을 필요하지만 소유는 아닐경우
  • 캐시나 백 레퍼런스
  • shared_ptr 구조에서 순환참조 방지
profile
이것 저것 다해보는 삶

0개의 댓글