Namespaces
Variants

std:: shared_ptr

From ko.cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
헤더 파일에 정의됨 <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
T (C++17 이전)
std:: remove_extent_t < T > (C++17 이후)
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)
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 와 비교합니다
(함수 템플릿)
저장된 포인터의 값을 출력 스트림에 출력합니다
(함수 템플릿)
std::swap 알고리즘을 특수화합니다
(함수 템플릿)
std::shared_ptr 에 대한 원자적 연산 전문화
(함수 템플릿)

헬퍼 클래스

원자적 공유 포인터
(클래스 템플릿 특수화)
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 이 관리하는 객체에 대한 약한 참조
(클래스 템플릿)