C++가 제공하는 똑똑한 포인터
C++의 스마트 포인터는 말 그대로 똑똑한 포인터로, 프로그래머가 발생시킬 수 있는 메모리 관리의 실수(메모리 누수, 댕글링 포인터 등)를 방지하기 위해서 설계된 도구이다. 그리고 그중에서 unique_ptr과 shared_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에 대해서는 추후에 알아보도록 하자.
'프로그래밍 > C++' 카테고리의 다른 글
| C++ 스마트 포인터 unique_ptr에 대해서 알아보자 (0) | 2026.01.02 |
|---|---|
| C++ Erase-remove 관용구란 무엇인가? (0) | 2025.12.28 |
| emplace() vs emplace_back()의 차이점 알아보기 (0) | 2025.12.22 |
| C++ emplace_back()을 무조건 권장하지 않는 이유는? (0) | 2025.12.22 |
| C++ 기본 타입은 왜 std::move()로 이동되지 않는가? (0) | 2025.12.20 |
댓글