오늘은 스마트 포인터 중...
두 번째인 shared_ptr에 대해서 알아볼거다
요즘 네이버 블챌 이벤트를 하고 있는데
가운데 정렬로 하니까 사진 첨부하기가 너무 좋았어서
왼쪽정렬파였는데 바꿔 보려고 한다 ^_^
++ 이전 글 올리고 다음 날 올릴려고 했는데
다른걸 먼저 올렸더니 잊혀질 뻔 했다
1. 스마트 포인터에는 뭐가 있지?
스마트 포인터는 3가지가 있다
1) unique_ptr
2) shared_ptr (오늘할거)
3) weak_ptr
1-1) unique_ptr이 뭐였더라 (링크 첨부 ^^)
첫 번째, unique_ptr은 포인터를 통해 Unique한 소유권을 가지고
unique_ptr 범위를 벗어날 때 해당 개체를 처리하는 스마트 포인터이다
두 번째, make_unique() 또는 new를 사용해서 생성할 수 있다
세 번째, 고유한 소유권이 있기 때문에, 복사 대신 move를 사용하여 구현한다
2. shared_ptr
unique_ptr은 고유한.... 유니크한 느낌이 드는 이름이었는데
shared는 뭔가 공유하는 공유될 것 같은 느낌이 든다

바로 사용 예시로 넘어가도록 하겠다
#include <memory>
#include <iostream>
using namespace std;
int main ( )
{
std::shared_ptr<int> ptr = std::make_shared<int>(10);
cout << ptr << endl;
cout << *ptr << endl;
return 0;
}
와~ ptr 출력하면 10이 나오겠지? ㅋ 하면 안된다
예시에서 사용한 것 처럼, ptr 자체를 출력하면 포인터기 때문에
포인터의 주소값이 나오고,
* 연산자를 사용하면 포인터가 가르키는 값인 10이 나온다
↓

또한 shared라는 이름에서 느낌이 오듯이
unique_ptr과 다르게 shared_ptr은 복사(copy)가 되고
같은 값을 가르키는 것도 가능하다
int main ( )
{
std::shared_ptr<int> ptr = std::make_shared<int>(10);
cout << *ptr << endl;
std::shared_ptr<int> ptr_cp = ptr;
cout << *ptr_cp << endl;
return 0;
}

ptr_cp라는 shared_ptr에 ptr의 값을 할당해도
원하는 결과가 나온 모습이다
요걸 그림으로 표현하면 아래와 같다

Diagram1을 보면, Control Block이란걸 p1이 참조하고 있다
여기서 p1은 shared_ptr 타입의 변수를 뜻하고
포인터니까 주소 값을 가진다(참조한다)고 이해하면 좋을 것 같다
2-1) Reference Count (참조 횟수)
shared_ptr에는 참조횟수라는 개념이 있다
unique_ptr과는 다르게 shared_ptr는 자기 포인터를 갖고 있는 동안
소유권을 공유할 수 있게 해준다
(포인터의 주소를 가져오고 싶을 때는 get()을 사용할 수 있다)
소유권이 늘어날 때(사용 횟수가 늘어날 때) 참조 횟수는 증가한다
또한 shared_ptr 객체를 소유하는 것이 아무도 없으면 참조 횟수도 0이 될 수 있다
참조 횟수는 use_count() 로 가져오면 된다
그림으로 봐보장

Diagram2를 보면, Diagram 1처럼 Control Block을 p1과 p2가 참조하고 있다
다른 점은, p1도 참조하고 → 참조 카운트 +1
p2도 참조하기 때문에 → 참조 카운트 +1
그림 상단의 Ref count가 2가 된 것을 볼 수 있다
참조하고 있는 p1과 p2가 모두 사라지면, Ref count도 0이 되는데
그러면 자동으로 삭제가 되어 메모리를 따로 해제하지 않아도 된다
말이 어렵지만 코드로 출력 몇 번 해보면 알 수 있다
int main ( )
{
std::shared_ptr<int> ptr = std::make_shared<int>(10);
cout << *ptr << endl;
std::shared_ptr<int> ptr_cp = ptr;
cout << *ptr_cp << endl;
std::shared_ptr<int> ptr_cp2 = ptr_cp;
cout << "Ref Count: " << ptr.use_count() << endl;
return 0;
}

ptr의 use_count가 3으로 출력이 됐다
1) ptr에 대해 make_shared로 만들어서 카운트 증가
2) ptr 포인터를 ptr_cp에서도 갖고 있음
3) ptr의 포인터를 갖는 ptr_cp의 포인터를 ptr_cp2에서도 갖고 있음
(ptr의 포인터를 가짐)
예시를 하나 더 들어보겠다
ptr을 사용안하면 참조 카운트도 자동으로 줄어드는지
확인하기 위해서, 지역 함수에서
shared_ptr 포인터를 복사하도록 해보겠다
void func(shared_ptr<int> ptr)
{
std::shared_ptr<int> ptr_cp = ptr;
cout << *ptr_cp << endl;
std::shared_ptr<int> ptr_cp2 = ptr_cp;
cout << __func__ << "::Ref Count: " << ptr.use_count() << endl;
}
int main ( )
{
std::shared_ptr<int> ptr = std::make_shared<int>(10);
cout << *ptr << endl;
func(ptr);
cout << __func__ << "::Ref Count: " << ptr.use_count() << endl;
return 0;
}

main에서는 shared_ptr 객체인 ptr만 만들어줬고
func() 함수에서 값 복사가 수행된다
그리고 파라미터로 포인터를 넘겨주면서 Ref count도 하나 더 증가했다
함수가 종료된 후에는 func()에서 수행한 참조는 없어졌기 때문에
main에서는 다시 Ref Count가 1로 되었다
3. shared_ptr를 value로 갖는 자료구조
shared_ptr은 map이나 vector 같은 자료구조에서
value 값으로도 많이 쓰인다
map을 감싸는 모양으로도 쓰이긴 하지만
포인터로 관리할 필요가 없다면 굳이?
바로 예시를 보면서 설명하도록 하겠다
#include <map>
int main ( )
{
map<string, shared_ptr<int>> mapTest;
auto ptr = make_shared<int>(10);
cout << __func__ << "::Ref Count: " << ptr.use_count() << endl;
mapTest["first"] = ptr;
cout << __func__ << "::Ref Count: " << ptr.use_count() << endl;
for( auto const &val : mapTest )
{
cout << val.first << ": " << *val.second << endl;
}
return 0;
}

이렇게 map의 value 값으로도 사용할 수 있다
map이 소멸된다면 value로 있는 shared_ptr의 소멸자도 자동으로 불린다
따로 관리 안해줘도 됨
편하게 유용하게 사용할 수 있지만
포인터는 포인터기 때문에 항상 조심하도록 하자
참고자료
https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count
std::shared_ptr::use_count - cppreference.com
long use_count() const noexcept; Returns the number of different shared_ptr instances (this included) managing the current object. If there is no managed object, 0 is returned. In multithreaded environment, the value returned by use_count is approxim
en.cppreference.com
'BackEnd > C랑 C++' 카테고리의 다른 글
Error 해결: boost::shared_ptr<boost::asio::io_context>::operator* (0) | 2022.02.04 |
---|---|
STRUCT와 UNION을 알아보자 (0) | 2021.07.12 |
Error 해결: undefined reference to `vtable for XXX' (0) | 2021.06.24 |
Boost-echo tcp client & server를 살펴보자 (1) (2) | 2021.06.11 |
C++ 스마트 포인터를 알아보자 - (1) unique_ptr (0) | 2021.05.25 |