[Modern C++ 14] Smart Pointer - std::weak_ptr
Modern C++ 14
주제 : Smart Pointer(std::unique_ptr, std::shared_ptr, std::weak_ptr)
헤더 : <memory>
설명
모던 C++에는 스마트 포인터 객체가 추가되었다. 기존 C 스타일의 포인터는 동적으로 생성된 객체들을 프로그래머가 잘 제어했어야 했다. 메모리가 잘 관리되지 않는다면 메모리 누수(memory leak), 댕글링 포인터(dangling pointer), 중복 삭제(double free) 등의 문제가 생길 수 있다. 스마트 포인터는 RAII(Resource Acquisition Is Initialization) 기법을 이용해 메모리 해제를 자동화시킴으로써 프로그래머의 실수를 줄여 보다 더 안전한 프로그래밍을 할 수 있게 되었다. 또한, 템플릿으로 작성되어, 어떤 타입이든 사용 가능하다. 스마트 포인터는 세 가지로 나뉘는 데, 이번 게시물에는 std::weak_ptr에 대해서 알아보도록 하겠다.
- std::weak_ptr
std::weak_ptr
도 std::shared_ptr
처럼 참조 횟수로 메모리를 관리하는 포인터이다. 두 개의 차이점은 std::shared_ptr
은 '소유'한다는 의미가 있는 데, std::weak_ptr
은 소유권을 주장하진 않고, 단순히 현재 객체가 사용되고 있는지, 아닌지에 대해서만 판별하기 위해 사용된다. 즉, std::shared_ptr
를 보조하기 위해 쓰이는 것이다. 좀 더 구체적으로 얘기하자면 순환 참조 고리 를 끊는 데에 사용 된다. 사용 방법은 다음과 같다.
#include <memory> #include <iostream> using namespace std; int main() { // 참조 횟수 1 증가. shared_ptr<int> sp = std::make_shared<int>(10); // 초기화 방법 // weak 참조 횟수 1 증가. weak_ptr<int> wp(sp); { // weak 참조 횟수 1 증가. 현재 2 // 참조 횟수는 변동 없음 weak_ptr<int> wp2 = wp; //weak_ptr<int> wp2(sp); //weak_ptr<int> wp2(wp); cout << "현재 참조 횟수 : " << sp.use_count() << endl; cout << "현재 weak 참조 횟수 : " << wp.use_count() << endl; } // weak 참조 횟수 1 감소. 현재 1 cout << "현재 weak 참조 횟수 : " << wp.use_count() << endl; if (shared_ptr<int> sp1 = wp.lock()) { cout << *sp1 << endl; } else { cout << "자원 획득 실패\n"; } // 참조 횟수 0. 자원 삭제 sp.reset(); if (wp.expired()) { cout << "자원이 삭제됨\n"; } return 0; // weak 참조 횟수 0. 매니저 객체 삭제 }
weak 참조 횟수라는 것을 얘기했는 데, 지난 std::shared_ptr에 대한 게시물에 이런 그림이 있었다.
실제로는 저 블록 참조에 std::shared_ptr
이 가리키는 횟수와 std::weak_ptr
이 가리키는 횟수로 나뉘어져 있다. VS2015 디버그 모드에서 다음과 같이 확인할 수 있다.
strong_ref가 std::shared_ptr
이 가리키는 횟수이고, weak_ref가 std::weak_ptr
이 가리키는 횟수이다. 블록 참조 객체는 strong_ref와 weak_ref 모두 0이어야 삭제되는 것이다.
주로 사용되는 메소드는 다음과 같다.
이름 | 설명 |
---|---|
expired | 자원이 삭제되었는지 점검한다. |
use_count | 참조 횟수를 반환한다. |
reset | 자원을 재설정 한다. |
swap | 인자로 전달된 포인터와 자원을 치환한다. |
lock | 자원에 대한 std::shared_ptr 객체를 생성한다. |
Terminology
- 순환 참조 고리
- 순환 참조 고리란, `std::shared_ptr`이 가리키는 객체가 서로서로 가리키게 되어 메모리가 해제되지 않는 현상을 말한다. 그림으로 보면 다음과 같다.
다음처럼 되어 있다고 할 때, 저 객체들을 가리키는 sp1, sp2, sp3가 소멸되었다고 하자. 그럼 우리는 각 객체들이 삭제되기를 기대할 것이다.
그럼 다음과 같아지는 데, 객체 내부에 있는 `std::shared_ptr`로 인해 참조 횟수가 남아 있으므로 기대한 것과 달리 객체가 삭제되지 않는다. 이를 끊기 위해 `std::weak_ptr`을 사용한다.
이런 식으로 사용해주면 객체에 대한 강한 참조 횟수가 0이므로 객체는 삭제되게 된다. 단, 블록 참조 객체는 삭제되지 않는다.