std:: shared_ptr
|
헤더 파일에 정의됨
<memory>
|
||
|
template
<
class
T
>
class
shared_ptr
;
|
(C++11부터) | |
std::shared_ptr
는 포인터를 통해 객체의 공유 소유권을 유지하는 스마트 포인터입니다. 여러 개의
shared_ptr
객체가 동일한 객체를 소유할 수 있습니다. 다음 중 하나가 발생하면 객체는 소멸되고 해당 메모리가 해제됩니다:
-
객체를 소유하는 마지막 남은
shared_ptr이 파괴될 때; -
객체를 소유하는 마지막 남은
shared_ptr이 operator= 또는 reset() 을 통해 다른 포인터로 재할당될 때.
객체는
delete-expression
또는 생성 시
shared_ptr
에 제공된 사용자 정의 삭제자를 사용하여 파괴됩니다.
A
shared_ptr
는 객체의 소유권을 공유하면서 다른 객체를 가리키는 포인터를 저장할 수 있습니다. 이 기능은 멤버 객체가 속한 객체를 소유하면서 해당 멤버 객체를 가리키는 데 사용될 수 있습니다. 저장된 포인터는
get()
, 역참조 및 비교 연산자로 접근되는 포인터입니다. 관리되는 포인터는 사용 카운트가 0에 도달했을 때 삭제자(deleter)에 전달되는 포인터입니다.
shared_ptr
는 객체를 소유하지 않을 수도 있으며, 이러한 경우 이를
empty
라고 합니다(별칭 생성자를 사용하여 생성된 경우, 빈
shared_ptr
이 null이 아닌 저장된 포인터를 가질 수 있습니다).
shared_ptr
의 모든 특수화는
CopyConstructible
,
CopyAssignable
, 그리고
LessThanComparable
요구사항을 충족하며
상황에 따라
bool
로 변환 가능합니다.
모든 멤버 함수(복사 생성자와 복사 할당 연산자 포함)는 서로 다른
shared_ptr
객체에서 추가적인 동기화 없이 여러 스레드에 의해 호출될 수 있으며, 이러한 객체들이 복사본이고 동일한 객체의 소유권을 공유하는 경우에도 마찬가지입니다. 만약 여러 실행 스레드가 동기화 없이 동일한
shared_ptr
객체에 접근하고 그 중 하나라도
shared_ptr
의 비상수 멤버 함수를 사용한다면 데이터 경쟁이 발생합니다;
std::atomic<shared_ptr>
를 사용하여 데이터 경쟁을 방지할 수 있습니다.
목차 |
멤버 타입
| 멤버 타입 | 정의 | ||||
element_type
|
|
||||
weak_type
(C++17 이후)
|
std:: weak_ptr < T > |
멤버 함수
새로운
shared_ptr
을 생성함
(public member function) |
|
더 이상
shared_ptr
이 연결되지 않으면 소유한 객체를 파괴함
(public member function) |
|
shared_ptr
을 할당함
(public member function) |
|
수정자 |
|
|
관리하는 객체를 교체함
(public member function) |
|
|
관리하는 객체들을 교환함
(public member function) |
|
관찰자 |
|
|
저장된 포인터를 반환함
(public member function) |
|
|
저장된 포인터를 역참조함
(public member function) |
|
|
(C++17)
|
저장된 배열에 대한 인덱스 접근을 제공함
(public member function) |
동일한 관리 객체를 참조하는
shared_ptr
객체의 수를 반환함
(public member function) |
|
|
(until C++20)
|
관리 객체가 현재
shared_ptr
객체에 의해서만 관리되는지 확인함
(public member function) |
|
저장된 포인터가 null이 아닌지 확인함
(public member function) |
|
|
shared pointer에 대한 소유자 기반 순서 지정을 제공함
(public member function) |
|
|
(C++26)
|
shared pointer에 대한 소유자 기반 해싱을 제공함
(public member function) |
|
(C++26)
|
shared pointer에 대한 소유자 기반 동등 비교를 제공함
(public member function) |
비멤버 함수
|
새로운 객체를 관리하는 shared_ptr을 생성합니다
(함수 템플릿) |
|
|
할당자를 사용하여 할당된 새로운 객체를 관리하는 shared_ptr을 생성합니다
(함수 템플릿) |
|
|
저장된 포인터에
static_cast
,
dynamic_cast
,
const_cast
, 또는
reinterpret_cast
를 적용합니다
(함수 템플릿) |
|
|
소유한 경우 지정된 타입의 삭제자를 반환합니다
(함수 템플릿) |
|
|
(C++20에서 제거됨)
(C++20에서 제거됨)
(C++20에서 제거됨)
(C++20에서 제거됨)
(C++20에서 제거됨)
(C++20)
|
다른
shared_ptr
또는
nullptr
와 비교합니다
(함수 템플릿) |
|
저장된 포인터의 값을 출력 스트림에 출력합니다
(함수 템플릿) |
|
|
(C++11)
|
std::swap
알고리즘을 특수화합니다
(함수 템플릿) |
std::shared_ptr
에 대한 원자적 연산 전문화
(함수 템플릿) |
헬퍼 클래스
|
(C++20)
|
원자적 공유 포인터
(클래스 템플릿 특수화) |
|
(C++11)
|
std::shared_ptr
에 대한 해시 지원
(클래스 템플릿 특수화) |
추론 가이드 (C++17부터)
참고 사항
객체의 소유권은 다른
shared_ptr
객체를 받는 생성자나 할당 함수를 통해서만 공유될 수 있습니다. 만약 새로운
shared_ptr
이 다른
shared_ptr
이 보유한 원시 기본 포인터만을 사용하여 생성된다면, 이 새로운
shared_ptr
은 자신이 소유한 객체를 다른
shared_ptr
인스턴스가 보유하고 있지 않다고 가정합니다. 이로 인해 (후속 할당이 발생하지 않는 한) 소멸 시 동일한 객체에 대해 deleter가 반복적으로 적용되는 결과를 초래합니다.
std::shared_ptr
는
incomplete type
T
와 함께 사용될 수 있습니다. 그러나 raw pointer로부터의 생성자 (
template
<
class
Y
>
shared_ptr
(
Y
*
)
) 와
template
<
class
Y
>
void
reset
(
Y
*
)
멤버 함수는 완전한 타입에 대한 포인터로만 호출될 수 있습니다 (
std::unique_ptr
은 incomplete type에 대한 raw pointer로부터 생성될 수 있음에 유의하십시오).
T
는 함수 타입일 수 있습니다: 이 경우 객체 포인터 대신 함수 포인터를 관리합니다. 이는 동적 라이브러리나 플러그인이 해당 함수들이 참조되는 동안 메모리에 유지되도록 할 때 종종 사용됩니다:
void del(void(*)()) {} void fun() {} int main() { std::shared_ptr<void()> ee(fun, del); (*ee)(); }
구현 참고 사항
일반적인 구현에서,
shared_ptr
은 단지 두 개의 포인터만 보유합니다:
- 저장된 포인터 ( get() 이 반환하는 값);
- control block 에 대한 포인터.
제어 블록은 다음과 같은 내용을 보유하는 동적으로 할당된 객체입니다:
- 관리되는 객체에 대한 포인터 또는 관리되는 객체 자체;
- 삭제자 (타입 삭제됨);
- 할당자 (타입 삭제됨);
-
관리되는 객체를 소유하는
shared_ptr의 개수; -
관리되는 객체를 참조하는
weak_ptr의 개수.
shared_ptr
가
std::make_shared
또는
std::allocate_shared
를 호출하여 생성될 때, 제어 블록과 관리 객체 모두에 대한 메모리가 단일 할당으로 생성됩니다. 관리 객체는 제어 블록의 데이터 멤버 내부에 인플레이스 방식으로 생성됩니다.
shared_ptr
생성자 중 하나를 통해
shared_ptr
가 생성될 때는 관리 객체와 제어 블록을 별도로 할당해야 합니다. 이 경우 제어 블록은 관리 객체에 대한 포인터를 저장합니다.
shared_ptr
가 직접 보유하는 포인터는
get()
에 의해 반환되는 포인터이며, 제어 블록이 보유하는 포인터/객체는 공유 소유자 수가 0에 도달할 때 삭제될 포인터입니다. 이 두 포인터는 반드시 동일하지 않을 수 있습니다.
shared_ptr
의 소멸자는 제어 블록의 공유 소유자 수를 감소시킵니다. 이 카운터가 0에 도달하면, 제어 블록은 관리되는 객체의 소멸자를 호출합니다. 제어 블록은
std::weak_ptr
카운터 또한 0에 도달할 때까지 자신을 할당 해제하지 않습니다.
기존 구현에서는 동일한 제어 블록을 가리키는 shared 포인터가 있는 경우 weak 포인터의 수가 증가합니다 ( [1] , [2] ).
스레드 안전성 요구사항을 충족시키기 위해, 참조 카운터는 일반적으로 std::atomic::fetch_add 와 std::memory_order_relaxed 를 사용하여 증가시킵니다 (제어 블록을 안전하게 파괴하기 위해 감소 시에는 더 강한 순서 지정이 필요합니다).
예제
#include <chrono> #include <iostream> #include <memory> #include <mutex> #include <thread> using namespace std::chrono_literals; struct Base { Base() { std::cout << "Base::Base()\n"; } // 참고: 비가상 소멸자가 여기서는 괜찮음 ~Base() { std::cout << "Base::~Base()\n"; } }; struct Derived : public Base { Derived() { std::cout << "Derived::Derived()\n"; } ~Derived() { std::cout << "Derived::~Derived()\n"; } }; void print(auto rem, std::shared_ptr<Base> const& sp) { std::cout << rem << "\n\tget() = " << sp.get() << ", use_count() = " << sp.use_count() << '\n'; } void thr(std::shared_ptr<Base> p) { std::this_thread::sleep_for(987ms); std::shared_ptr<Base> lp = p; // 스레드 안전함, 공유 use_count가 증가해도 // 안전 { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); print("스레드 내 로컬 포인터:", lp); } } int main() { std::shared_ptr<Base> p = std::make_shared<Derived>(); print("공유 Derived 생성 (Base에 대한 포인터로)", p); std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset(); // 메인에서 소유권 해제 print("3개 스레드 간 공유 소유권 및 메인에서 소유권 해제:", p); t1.join(); t2.join(); t3.join(); std::cout << "모든 스레드 완료, 마지막 스레드가 Derived를 삭제함.\n"; }
가능한 출력:
Base::Base() Derived::Derived() Created a shared Derived (as a pointer to Base) get() = 0x118ac30, use_count() = 1 Shared ownership between 3 threads and released ownership from main: get() = 0, use_count() = 0 Local pointer in a thread: get() = 0x118ac30, use_count() = 5 Local pointer in a thread: get() = 0x118ac30, use_count() = 4 Local pointer in a thread: get() = 0x118ac30, use_count() = 2 Derived::~Derived() Base::~Base() All threads completed, the last one deleted Derived.
예제
#include <iostream> #include <memory> struct MyObj { MyObj() { std::cout << "MyObj 생성됨\n"; } ~MyObj() { std::cout << "MyObj 소멸됨\n"; } }; struct Container : std::enable_shared_from_this **변역 결과:** std::enable_shared_from_this **설명:** - HTML 태그와 속성은 그대로 유지되었습니다. - C++ 관련 용어(`std::enable_shared_from_this`)는 번역하지 않고 원문을 그대로 유지했습니다. - 태그 내부의 텍스트도 모두 보존되었습니다.<Container> // 참고: public 상속 { std::shared_ptr<MyObj> memberObj; void CreateMember() { memberObj = std::make_shared<MyObj>(); } std::shared_ptr<MyObj> GetAsMyObj() { // 멤버에 대한 별칭 공유 포인터 사용 return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get()); } }; #define COUT(str) std::cout << '\n' << str << '\n' #define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n' int main() { COUT("공유 컨테이너 생성"); std::shared_ptr<Container> cont = std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("멤버 생성"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("다른 공유 컨테이너 생성하기"); std::shared_ptr<Container> cont2 = cont; DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("GetAsMyObj"); std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("별칭 객체 복사 중"); std::shared_ptr<MyObj> myobj2 = myobj1; DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("cont2 재설정 중"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("myobj2 재설정 중"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("컨트 재설정 중"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); }
출력:
공유 컨테이너 생성 cont.use_count() = 1 cont->memberObj.use_count() = 0 멤버 생성 MyObj constructed cont.use_count() = 1 cont->memberObj.use_count() = 1 다른 공유 컨테이너 생성 cont.use_count() = 2 cont->memberObj.use_count() = 1 cont2.use_count() = 2 cont2->memberObj.use_count() = 1 GetAsMyObj myobj1.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 cont2.use_count() = 3 cont2->memberObj.use_count() = 1 별칭 객체 복사 myobj1.use_count() = 4 myobj2.use_count() = 4 cont.use_count() = 4 cont->memberObj.use_count() = 1 cont2.use_count() = 4 cont2->memberObj.use_count() = 1 cont2 리셋 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 myobj2 리셋 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 cont 리셋 myobj1.use_count() = 1 cont.use_count() = 0 MyObj destructed
참고 항목
|
(C++11)
|
고유 객체 소유권 의미론을 가진 스마트 포인터
(클래스 템플릿) |
|
(C++11)
|
std::shared_ptr
이 관리하는 객체에 대한 약한 참조
(클래스 템플릿) |