본문 바로가기
프로그래밍/C++

unique_ptr vs shared_ptr 차이는 무엇인가?

by bantomak 2026. 1. 3.
반응형

C++가 제공하는 똑똑한 포인터

C++의 스마트 포인터는 말 그대로 똑똑한 포인터로, 프로그래머가 발생시킬 수 있는 메모리 관리의 실수(메모리 누수, 댕글링 포인터 등)를 방지하기 위해서 설계된 도구이다. 그리고 그중에서 unique_ptrshared_ptr은 핵심이 되는 기능들이다.

std::unique_ptr 독점적 소유권

"객체의 주인은 오직 하나여야 한다"라는 원칙을 따른다. 가비지 컬렉션이 없는 C++에서 가장 효율적으로 자원을 관리하기 위해, 포인터가 스코프(Scope)를 벗어나면 즉시 소멸자를 호출하여 메모리를 해제한다. 성능 오버헤드가 거의 없어 일반 포인터와 차이가 없다.

  • 복사 불가: 실수로 복사해서 소유권이 꼬이는 것을 문법적으로 막는다.
  • 이동 가능: std::move를 통해서 소유권을 다른 곳으로 넘길 수 있다.
  • 추가적인 메모리나 연산이 필요없는 가장 가벼운 스마트 포인터
{
	std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();

	std::unique_ptr<MyClass> ptr2(ptr); // 복사 생성자 불가
	std::unique_ptr<MyClass> ptr2 = ptr; // 복사 대입 연산자 불가
	std::unique_ptr<MyClass> ptr3 = std::move(ptr); // 소유권 이전은 가능

	if (!ptr)
	{
		std::cout << "ptr 소유권 이전됨" << std::endl;
	}

} // <- 이 범위를 벗어나며 소멸함
  // 이때 내부의 ptr3도 자동으로 delete함

memory.h 소스 찾아가기

헤더를 살펴보면 복사 생성자와 복사 대입 연산자가 delete로 선언된 것을 확인할 수 있다.

unique_ptr(const unique_ptr&)            = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

std::shared_ptr 공유 소유권

"더 이상 사용하는 곳이 없을 때 자원을 해제한다."라는 철학을 가진다. shared_ptr은 내부적으로 참조 횟수(Reference Count)를 관리하는 제어 블록(Control Block)을 만들어서, 참조하는 포인터가 0이 되는 순간 자원을 해제한다.

  • 공유 가능: 여러 객체가 동일한 자원을 참조해야 하는 복잡한 자료구조(그래프 등)에서 유용하다.
  • 유연성: 자원의 생명 주기가 불분명할 때 안전한 대안이 된다.
  • 참조 횟수를 관리하기 위한 약간의 메모리 오버헤드와 원자적 연산 비용이 발생한다.
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();

{
	std::shared_ptr<MyClass> ptr2(ptr); // 복사 연산자 가능
	std::shared_ptr<MyClass> ptr3 = ptr; // 복사 대입 연산자 가능
	std::shared_ptr<MyClass> ptr4 = std::move(ptr); // 소유권 이전 됨

	if (!ptr)
	{
		std::cout << "ptr 소유권 이전됨" << std::endl;
	}

	std::cout << ptr4.use_count() << std::endl; // 참조 카운트 3

} // <- 이 범위를 벗어나면 소멸함

std::cout << ptr.use_count() << std::endl; // 참조 카운트 0

정리하자면

  • std::unique_ptr
    • 독점적(Exclusive) 소유권
    • 복사 불가능
    • 오버헤드 거의 없음
    • 클래스 내부 변수, 지역 변수처럼 사용
  • std::shared_ptr
    • 공유(Shared) 소유권
    • 복사 가능
    • 참조 횟수 관리 비용 발생
    • 전역 관리, 복잡한 객체 공유

기본적으로 스마트 포인터 사용시에는 unique_ptr을 먼저 고려해야 한다. 소유권이 독점적이기 때문에 사용하기 쉽고 문제를 찾아서 해결하기도 좋다. 게다가 성능은 덤이다. 그 이외에 여러 곳에서 동시에 참조해야 한다면, 누가 마지막에 소멸될지 알 수 없기 때문에 이때에는 shared_ptr의 사용을 고려해야 한다. 사용 시 순환 참조가 일어나는 문제가 생기는 경우가 있는데 이때는 weak_ptr을 사용하면 된다. weak_ptr에 대해서는 추후에 알아보도록 하자.

댓글