C++ Chapter 15.6 : 스마트 포인터2️⃣ std::shared_ptr
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
chapter 15. 의미론적 이동과 스마트 포인터
15.6 스마트 포인터2️⃣ std::shared_ptr
#include <memory>
🚀 참고
내가 쓴 답변이다. 이 블로그 이 이 답변을 작성하는데에 도움 되었다. std::shared_ptr
에 대해 더 자세한 것은 링크 된 블로그에서 참고하기.
🔔 std::shared_ptr
객체에 대한 소유권을 나눠 주고 공유 한다.
- 소유한 객체를 자동으로
delete
해주는 스마트 포인터다. - unique_ptr과 달리 ✨여러 shared_ptr이 한 객체에 대해 동시에 공유(shared)할 수 있도록 한다.✨
- unique_ptr은 소유권을 엄격히 관리할 때 사용
- shared_ptr은 소유권을 느슨하게 관리할 때 사용
- 소유권을 여러
shared_ptr
가 동시에 가질 수 있기 때문에 그 중 한 포인터로delete
되더라도 그 객체는 소멸되지 않는다. 소유권만 박탈될 뿐이다. 그 객체를 소유한 포인터가 하나 밖에 없을 때만delete
된다.
- 소유권을 여러
- 또한
shared_ptr
은 자신이 가리키고 있는 객체가 몇 군데서 공유되고 있는지 내부적으로 항상 세고 있다.
소유권 분배가 제대로 이루어 졌을 때
#include <iostream>
#include <memory>
int main()
{
Resource * res = new Resource(3);
res->setAll(1);
std::shared_ptr<Resource> ptr1(res);
ptr1 -> print();
{
std::shared_ptr<Resource> ptr2(ptr1);
ptr2->setAll(5);
ptr2->print();
std::cout << "Going out of the block" << std::endl;
}
ptr1->print();
std::cout << "Going out of the block" << std::endl;
return 0;
}
💎출력💎
Resource length constructed
1 1 1
5 5 5
Going out of the block
5 5 5
Going out of the block
Resource destroyed
- Resource 클래스 코드는 이전 포스트에 있습니다.
-
- 소유권 분배 👉 std::shared_ptr<Resource>
ptr2(ptr1)
;std::shared_ptr<Resource> ptr1(res); std::shared_ptr<Resource> ptr2(ptr1);
- 👉std::shared_ptr<Resource>
ptr2(ptr1)
; - ptr1이 가리키고 있던 Resource 객체의 소유권을 ptr2에게도 분배한다.
ptr1
와ptr2
이 동일한 객체를 가리키게 된다. 동시에 소유권을 가진다.
- 👉std::shared_ptr<Resource>
ptr2(ptr1)
👉 ⭐ptr1에게도 소유권이 있다는 것을 ptr2 가 알고 있다.- 따라서
ptr2
이 블록 밖을 벗어나서 delete 되더라도 여전히 ptr1과 함께 소유했던 그 res 객체는 사라지지 않는다. ptr1
과ptr2
이 unique_ptr 이었으면ptr2
이 사라짐과 동시에res
객체도 delete 되었을 것이다.- 객체의 유일한 소유권자가 사라질 때 그제서야 delete 된다.
- 따라서
- 소유권 분배 👉 std::shared_ptr<Resource>
소유권 분배가 제대로 이루어지지 않았을 때
int main()
{
Resource * res = new Resource(3);
res->setAll(1);
std::shared_ptr<Resource> ptr1(res);
ptr1->print();
{
std::shared_ptr<Resource> ptr2(res);
ptr2->setAll(5);
ptr2->print();
std::cout << "Going out of the block" << std::endl;
}
ptr1->print(); // 📢 런타임 에러 발생!!
std::cout << "Going out of the block" << std::endl;
return 0;
}
💎출력💎
Resource length constructed
1 1 1
5 5 5
Going out of the block
Resource destroyed
-
- 소유권 분배가 제대로 이루어지지 않는다. 👉 std::shared_ptr<Resource>
ptr2(res)
;std::shared_ptr<Resource> ptr1(res); std::shared_ptr<Resource> ptr2(res);
ptr1
과ptr2
이res
라는 동일한 객체를 가리키고 소유하고 있지만 ⭐ptr1
,ptr2
가 둘 다 res에 대한 소유권이 있는 상태라는 것을 서로 알 수가 없다.- 따라서
ptr2
이 블록 밖을 나오면서 delete 되면res
객체까지도 해제되기 때문에ptr1->print();
에서 런타임 에러가 발생한다. 없어진 객체에 접근하려고 하는 거니까!
- 따라서
- 소유권 분배가 제대로 이루어지지 않는다. 👉 std::shared_ptr<Resource>
std::shared_ptr<Resource> ptr1(res);
std::shared_ptr<Resource> ptr2(res);
auto ptr2 = ptr1;
- shared_ptr 포인터 선언시 객체에 대해 정의할 것이라면
- 👉
ptr2 = ptr1
이런 복사 방식을 통하여 ptr1, ptr2 둘 다 동일한 객체에 대한 소유권을 공유하고 있음을 서로에게 알려주어야 한다.- ptr1 과 ptr2 는 각각 다른 제어 블록을 참조하는 상태
- 👉
🔔 std::shared_ptr 관련 함수들
std::make_shared 함수
위와 같은 상황 때문에 make_shared 함수를 통하여 shared_ptr을 안전하게 생성할 수 있다.
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>(3);
std::shared_ptr<Resource> ptr2 = ptr1 // ✨소유권을 공유하고 있음을 알려주어야 함.
// auto를 써주면 더 간단! 위와 같은 코드.
auto ptr1 = std::make_shared<Resource>(3);
auto ptr2 = ptr1;
- Resource * res = new Resource(3); 과정이 필요 없다.
make_shared
함수를 통하여 객체 생성을 하기 때문에!
- ptr1 = std::make_shared<Resource>(3);
- 1️⃣ Resource 객체를 생성함과 동시에 2️⃣ 이를 소유하는
shared_ptr
스마트 포인터를 리턴한다.
- 1️⃣ Resource 객체를 생성함과 동시에 2️⃣ 이를 소유하는
- make_shared 함수를 통해 객체 생성과 동시에 이를 소유하는
shared_ptr
스마트 포인터를 생성하는 것을 권장한다. - ptr1 과 ptr2 는 같은 제어 블록을 참조하는 상태
use_count(), reset() 함수
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>(3);
cout << "현재 소유자 수 : " << ptr1.use_count() << endl; // 1 출력
auto ptr2 = ptr1;
cout << "현재 소유자 수 : " << ptr1.use_count() << endl; // 2 출력
ptr2.reset();
cout << "현재 소유자 수 : " << ptr1.use_count() << endl; // 1 출력
use_count()
: 어떤 객체를 소유하고 있는 shared_ptr 포인터들이 현재 총 몇 개인지 리턴해준다.
- 여러 shared_ptr 포인터들이 하나의 객체를 나눠서 소유하고 있기 때문에 소유권을 가진 포인터가 아무것도 없을 때만, 즉
use_count()
결과가 0 일때만 객체가 delete 될 수 있도록 늘 소유권을 가진 shared_ptr 포인터가 총 몇개인지 세고 있다.
reset()
: 해당 shared_ptr 포인터의 객체에 대한 소유권을 박탈한다.
🔔 shared_ptr의 문제점
- 순환 참조
- 보통 그룹 객체와 소속 객체 사이에서 발생하는 순홤 참조.
- 서로가 서로를 참조하는 순환 참조가 가능한데 이런 상황에선 메모리 해제가 제대로 되지 않는다.
- 멀티 쓰레드 안정성
- 다수의 쓰레드에서 같은 객체를 참조하는 경우가 있을 수 있다. 이때 읽기는 안전하지만 쓰기는 안전하지 않다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기