Namespaces
Variants

std:: make_shared, std:: make_shared_for_overwrite

From 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 ... Args >
shared_ptr < T > make_shared ( Args && ... args ) ;
(1) (C++11부터)
template < class T >
shared_ptr < T > make_shared ( std:: size_t N ) ;
(2) (C++20부터)
template < class T >
shared_ptr < T > make_shared ( ) ;
(3) (C++20부터)
template < class T >
shared_ptr < T > make_shared ( std:: size_t N, const std:: remove_extent_t < T > & u ) ;
(4) (C++20부터)
template < class T >
shared_ptr < T > make_shared ( const std:: remove_extent_t < T > & u ) ;
(5) (C++20부터)
template < class T >
shared_ptr < T > make_shared_for_overwrite ( ) ;
(6) (C++20부터)
template < class T >
shared_ptr < T > make_shared_for_overwrite ( std:: size_t N ) ;
(7) (C++20부터)

객체에 대한 메모리를 할당하고 제공된 인수로 객체를 초기화합니다. 새로 생성된 객체를 관리하는 std::shared_ptr 객체를 반환합니다.

1) 객체는 T 타입이며, 다음과 같이 생성됩니다: :: new ( pv ) T ( std:: forward < Args > ( args ) ... ) , 여기서 pv T 타입의 객체를 보관하기에 적합한 저장 공간을 가리키는 void * 포인터입니다. 객체가 파괴되어야 하는 경우, 다음과 같이 파괴됩니다: pt - > ~T ( ) , 여기서 pt T 타입의 해당 객체를 가리키는 포인터입니다.

이 오버로드는 T 가 배열 타입이 아닌 경우에만 오버로드 해결에 참여합니다.

(since C++20)
2) 객체의 타입은 std:: remove_extent_t < T > [ N ] 입니다. 각 요소는 기본 초기값을 가집니다.
이 오버로드는 T 가 경계가 없는 배열 타입인 경우에만 오버로드 해결에 참여합니다.
3) 객체는 T 타입입니다. 각 요소는 기본 초기값을 가집니다.
이 오버로드는 T 가 경계 있는 배열 타입인 경우에만 오버로드 해결에 참여합니다.
4) 객체의 타입은 std:: remove_extent_t < T > [ N ] 입니다. 각 요소는 초기값 u 를 가집니다.
이 오버로드는 T 가 경계가 없는 배열 타입인 경우에만 오버로드 해결에 참여합니다.
5) 객체는 T 타입입니다. 각 요소는 초기값 u 을 가집니다.
이 오버로드는 T 가 경계 있는 배열 타입인 경우에만 오버로드 해결에 참여합니다.
6) 객체는 T 타입입니다.
  • T 가 배열 타입이 아닌 경우, 객체는 :: new ( pv ) T 와 같은 방식으로 생성됩니다. 여기서 pv void * 포인터로 T 타입 객체를 담기에 적합한 저장 공간을 가리킵니다. 객체가 파괴되어야 하는 경우, pt - > ~T ( ) 와 같은 방식으로 파괴됩니다. 여기서 pt T 타입의 해당 객체를 가리키는 포인터입니다.
  • T 가 경계가 지정된 배열 타입인 경우, 각 요소의 초기값은 지정되지 않습니다.
이 오버로드는 T 가 배열 타입이 아니거나 경계가 지정된 배열 타입인 경우에만 오버로드 해결에 참여합니다.
7) 객체의 타입은 std:: remove_extent_t < T > [ N ] 입니다. 각 요소의 초기값은 지정되지 않습니다.
이 오버로드는 T 가 무한 배열 타입인 경우에만 오버로드 해결에 참여합니다.

목차

배열 요소 초기화 및 소멸

U 타입의 배열 요소는 주소의 오름차순으로 초기화됩니다.

  • U 가 배열 타입이 아닌 경우, 각 요소는 다음 표현식과 같이 생성됩니다. 여기서 pv U 타입 객체를 저장하기에 적합한 저장 공간을 가리키는 void * 포인터입니다:
2,3) :: new ( pv ) U ( )
4,5) :: new ( pv ) U ( u )
6,7) :: new ( pv ) U
  • 그렇지 않은 경우, 각 요소의 요소들을 재귀적으로 초기화합니다. 다음 차원에 대해:
  • U std:: remove_extent_t < U > 가 됩니다.
  • 오버로드 (4,5) 의 경우, u u 의 해당 요소가 됩니다.

반환된 std::shared_ptr 가 관리하는 객체의 수명이 종료되거나 배열 요소 초기화에서 예외가 발생하면, 초기화된 요소들은 원래 생성 순서의 역순으로 소멸됩니다.

소멸될 비배열 타입 U 의 각 배열 요소에 대해, 다음 표현식과 같이 소멸됩니다: pu - > ~U ( ) , 여기서 pu U 타입의 해당 배열 요소를 가리키는 포인터입니다.

(C++20부터)

매개변수

args - T 객체가 생성될 인수들의 목록
N - 사용할 배열 크기
u - 배열의 모든 요소를 초기화할 초기값

반환값

std::shared_ptr 타입 T 의 객체에 대한 또는 std:: remove_extent_t < T > [ N ] (만약 T 가 무제한 배열 타입인 경우) (C++20부터) .

반환된 std::shared_ptr r 에 대해, r. get ( ) 는 널이 아닌 포인터를 반환하고, r. use_count ( ) 1 을 반환합니다.

예외

std::bad_alloc 을 던질 수 있으며, T 의 생성자에 의해 던져지는 모든 예외를 던질 수 있습니다. 예외가 발생하면 함수는 아무런 효과를 가지지 않습니다. 배열 생성 중 예외가 발생하면, 이미 초기화된 요소들은 역순으로 소멸됩니다. (C++20부터)

참고 사항

이러한 함수들은 일반적으로 내부 기록 관리 구조(예: 참조 카운트)를 허용하기 위해 sizeof ( T ) 보다 더 많은 메모리를 할당합니다.

이러한 함수들은 std:: shared_ptr < T > ( new T ( args... ) ) 의 대안으로 사용될 수 있습니다. 장단점은 다음과 같습니다:

  • std:: shared_ptr < T > ( new T ( args... ) ) 는 최소 두 번의 할당(객체 T 용 한 번과 shared pointer의 제어 블록용 한 번)을 수행하는 반면, std :: make_shared < T > 는 일반적으로 단 한 번의 할당만 수행합니다(표준에서 이를 권장하지만 요구하지는 않으며, 알려진 모든 구현체들이 이렇게 동작합니다).
  • 모든 shared 소유자의 수명이 끝난 후에도 std::make_shared 로 생성된 제어 블록을 참조하는 std::weak_ptr 이 존재한다면, T 가 차지하던 메모리는 모든 weak 소유자도 파괴될 때까지 유지됩니다. 이는 sizeof ( T ) 가 클 경우 바람직하지 않을 수 있습니다.
  • std:: shared_ptr < T > ( new T ( args... ) ) 는 접근 가능한 컨텍스트에서 실행될 경우 T 의 비공개 생성자를 호출할 수 있지만, std::make_shared 는 선택된 생성자에 대한 공개 접근 권한이 필요합니다.
  • std::shared_ptr 생성자들과 달리, std::make_shared 는 사용자 정의 삭제자를 허용하지 않습니다.
  • std::make_shared :: new 를 사용하므로, 클래스별 operator new 를 사용하여 특별한 동작이 설정된 경우 std:: shared_ptr < T > ( new T ( args... ) ) 와 다르게 동작할 수 있습니다.
(C++20 이전)
  • 다음과 같은 코드 f ( std:: shared_ptr < int > ( new int ( 42 ) ) , g ( ) ) g new int ( 42 ) 이후에 호출되어 예외를 발생시키는 경우 메모리 누수가 발생할 수 있지만, f ( std :: make_shared < int > ( 42 ) , g ( ) ) 는 안전합니다. 왜냐하면 두 함수 호출이 절대 인터리빙되지 않기 때문입니다.
(C++17까지)

생성자가 shared_from_this 을 활성화한다는 것은 ptr 타입의 포인터 U* 를 사용하여 U 명확하고 접근 가능한 (C++17부터) std::enable_shared_from_this 의 특수화인 베이스 클래스를 가지고 있는지 판단하고, 만약 그렇다면 생성자가 다음을 평가한다는 의미입니다: if ( ptr ! = nullptr && ptr - > weak_this  . expired ( ) )
ptr - > weak_this = std:: shared_ptr < std:: remove_cv_t < U >>
( * this, const_cast < std:: remove_cv_t < U > * > ( ptr ) ) ;
.

weak_this 에 대한 할당은 원자적이지 않으며 동일한 객체에 대한 잠재적 동시 접근과 충돌합니다. 이는 향후 shared_from_this() 호출이 이 원시 포인터 생성자로 생성된 std::shared_ptr 와 소유권을 공유하도록 보장합니다.

위 코드의 테스트 ptr - > weak_this  . expired ( ) weak_this 가 이미 소유자를 가리키고 있을 경우 재할당되지 않도록 보장합니다. 이 테스트는 C++17부터 필수적으로 요구됩니다.

기능 테스트 매크로 표준 기능
__cpp_lib_shared_ptr_arrays 201707L (C++20) std::make_shared 의 배열 지원; 오버로드 ( 2-5 )
__cpp_lib_smart_ptr_for_overwrite 202002L (C++20) 기본 초기화를 사용한 스마트 포인터 생성 ( std::allocate_shared_for_overwrite , std::make_shared_for_overwrite , std::make_unique_for_overwrite ); 오버로드 ( 6,7 )

예제

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
struct C
{
    // 생성자 필요 (C++20까지)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};
int main()
{
    // "sp1"의 타입에 "auto" 사용
    auto sp1 = std::make_shared<C>(1); // 오버로드 (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
    // "sp2"의 타입을 명시적으로 지정
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // 오버로드 (1)
    static_assert(std::is_same_v<decltype(sp2), std::shared_ptr<C>>);
    static_assert(std::is_same_v<decltype(sp1), decltype(sp2)>);
    std::cout << "sp2->{ i:" << sp2->i << ", f:" << sp2->f << " }\n";
    // 값-초기화된 float[64]에 대한 shared_ptr; 오버로드 (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
    // 값-초기화된 long[5][3][4]에 대한 shared_ptr; 오버로드 (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
    // 값-초기화된 short[128]에 대한 shared_ptr; 오버로드 (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
    // 값-초기화된 int[7][6][5]에 대한 shared_ptr; 오버로드 (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
    // double[256]에 대한 shared_ptr, 각 요소는 2.0; 오버로드 (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
    // shared_ptr to a double[7][2], where each double[2]
    // element is {3.0, 4.0}; overload (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
    // shared_ptr to a vector<int>[4], where each vector
    // 내용이 {5, 6}임; 오버로드 (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
    // shared_ptr to a float[512], where each element is 1.0; overload (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
    // shared_ptr to a double[6][2], where each double[2] element
    // is {1.0, 2.0}; overload (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
    // shared_ptr to a vector<int>[4], where each vector
    // 내용이 {5, 6}임; 오버로드 (5):
    std::shared_ptr<std::vector<int>[4]> spC =
        std::make_shared<std::vector<int>[4]>({5, 6});
}

출력:

sp1->{ i:1, f:0 }
sp2->{ i:2, f:3 }

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
LWG 4024 C++20 std::make_shared_for_overwrite 에서 생성된 객체가
어떻게 파괴되는지 불분명했음
명확히 규정됨

참고 항목

새로운 shared_ptr 을 생성함
(public member function)
할당자를 사용하여 할당된 새 객체를 관리하는 shared pointer를 생성함
(function template)
객체가 자신을 참조하는 shared_ptr 을 생성할 수 있도록 함
(class template)
새 객체를 관리하는 unique pointer를 생성함
(function template)
할당 함수들
(function)