shared_ptr은 unique_ptr과 달리 여러개의 스마트 포인터가 하나의 객체를 같이 소유하는 경우에 사용한다. 이 경우에는 특정 자원을 몇개의 객체에서 가리키는지 추적한 다음 그 수가 0이 되면 해제를 해주는 방식이다. WinApi 게임 프레임워크에서 사용했던 레퍼런스 카운트와 같은 방식이다.

여러개의 shared_ptr들은 같은 객체를 가리킬 수 있고, 같은 객체를 가리키는 shared_ptr의 수만큼 레퍼런스 카운트가 증가한다. 참조 개수가 몇 개 인지는 use_count 함수를 통해 알 수 있다.

shared_ptr들이 레퍼런스 카운트를 저장하는 방식은, 제어 블록을 이용한다. 제어 블록을 동적으로 할당하고 shared_ptr들이 필요한 정보를 공유한다. shared_ptr를 복사 생성할 때마다 제어 블록의 위치를 공유하고 레퍼런스 카운트를 증가시킨다.
std::shared_ptr<A> p1(new A()); //동적할당 두번
std::shared_ptr<A> p1 = std::make_shared<A>(); //동적할당 한번
shared_ptr을 생성할 때는 make_shared를 사용하는 것이 좋다. 첫 줄의 코드는 A를 생성하기 위해 동적 할당을 한번 하고 shared_ptr의 제어 블록을 동적으로 할당해야 하기 때문에 두번의 동적 할당이 발생한다. make_shared 함수는 객체 A와 제어블록까지 한번에 동적 할당을 한다. 동적 할당은 상당히 비싼 연산이기 때문에 두개 합친 크기로 한번 할당하는 것이 훨씬 빠르다.
A* a = new A();
std::shared_ptr<A> pa1(a);
std::shared_ptr<A> pa2(a);

코드를 위와 같이 작성하면 두개의 제어 블록이 따로 생성된다. 그래서 p1이 소멸하면 가리키는 객체 A를 소멸 시키기 때문에 p2가 소멸할 때 이미 해제된 객체를 소멸시키며 오류가 발생한다. 그래서 주소값을 통해 shared_ptr을 생성하는 것을 지양해야 한다.
class A : public std::enable_shared_from_this<A>
std::shared_ptr<A> pa2 = pa1->get_shared_ptr();
this를 사용해서 shared_ptr을 만들고 싶다면 make_shared<A>(this)는 위와 같이 두 개의 제어블록을 생성하기 때문에 enable_shared_from_this를 상속받아 get_shared_ptr()로 만들어줘야 한다.
'C++' 카테고리의 다른 글
| [C++] weak_ptr (0) | 2022.02.24 |
|---|---|
| [C++] override 지정자 (0) | 2022.02.22 |
| [C++] 스마트 포인터, unique_ptr (0) | 2022.02.18 |
| [C++] 파일 입출력 - fopen_s, fread, fwrite (0) | 2022.01.25 |
| [C++] 가변 인자 템플릿 (variadic template) (0) | 2022.01.22 |