배열에 shared_ptr : 사용해야합니까?


172

에 관한 작은 쿼리 shared_ptr입니다.

shared_ptr배열을 가리키는 것을 사용하는 것이 좋습니다 ? 예를 들어

shared_ptr<int> sp(new int[10]);

그렇지 않다면 왜 안됩니까? 내가 이미 알고있는 한 가지 이유는를 늘리거나 줄일 수 없기 때문 shared_ptr입니다. 따라서 배열에 대한 일반적인 포인터처럼 사용할 수 없습니다.


2
FWIT,을 사용하는 것도 고려할 수 있습니다 std::vector. 참조를 사용하여 배열을 전달하여 사본을 만들지 않도록주의해야합니다. 데이터 액세스 구문은 shared_ptr보다 깨끗하며 크기를 조정하는 것은 매우 쉽습니다. 그리고 원하는 모든 STL 장점을 얻을 수 있습니다.
Nicu Stiurca

6
컴파일 타임에 배열의 크기가 결정되면를 사용하는 것도 고려할 수 있습니다 std::array. 그것은 인 거의 대부분 라이브러리 요소에 사용하기에 적절한 의미로하지만, 원료와 같은 배열. 특히 해당 유형의 객체는로 삭제 delete되지 않습니다 delete[]. 와 달리 vector데이터는 객체에 직접 저장되므로 추가 할당이 없습니다.
celtschk

답변:


268

함께 C ++ 17 , shared_ptr동적으로 할당 된 어레이를 관리하기 위해 사용될 수있다. shared_ptr이 경우 템플릿 인수해야 T[N]하거나 T[]. 그래서 당신은 쓸 수 있습니다

shared_ptr<int[]> sp(new int[10]);

n4659부터 [util.smartptr.shared.const]

  template<class Y> explicit shared_ptr(Y* p);

요구 사항 : Y 완전한 유형이어야한다. , 표현식 이 배열 유형일 때 또는 배열 유형이 아닌 delete[] p경우 표현식 은 올바르게 정의 된 동작을 가지며 예외를 발생시키지 않습니다. ... 설명 : 때 어레이 타입 표현이없는 한,이 생성자 과부하 해상도에 참여하지 않아야한다 잘 형성되고 하나 이며 하고 로 변환 가능한 , 또는 인 및 으로 변환이다 . ...Tdelete pT

Tdelete[] pTU[N]Y(*)[N]T*TU[]Y(*)[]T*

이를 지원하기 위해 멤버 유형 element_type이 이제

using element_type = remove_extent_t<T>;

배열 요소는 다음을 사용하여 액세스 할 수 있습니다. operator[]

  element_type& operator[](ptrdiff_t i) const;

필요합니다 : get() != 0 && i >= 0 . 경우 T이다 U[N], i < N. ...
설명 : 경우 T배열 형이 아니라,이 멤버 함수가 선언되는지 여부를 지정한다. 선언 된 경우, 함수의 선언 (정의 일 필요는 없지만)이 올바르게 형성되는 것을 제외하고는 리턴 유형이 무엇인지 지정되지 않습니다.


앞서 C ++ 17 , shared_ptr없습니다 동적으로 할당 된 배열을 관리 할 수. 기본적으로 더 이상 참조가 남아 있지 않으면 관리 대상 객체를 shared_ptr호출 delete합니다. 사용 할당 할 때, new[]당신은 호출 할 필요가 delete[]아닌 delete, 리소스를 확보 할 수 있습니다.

shared_ptr어레이와 함께 올바르게 사용하려면 사용자 정의 삭제기를 제공해야합니다.

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

다음과 같이 shared_ptr을 작성하십시오.

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

이제 관리 대상 객체를 삭제할 때 shared_ptr올바르게 호출 delete[]합니다.

위의 맞춤 삭제 프로그램은 다음으로 대체 될 수 있습니다.

  • std::default_delete어레이 타입 부분 특수화

    std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
  • 람다 표현

    std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });

또한 실제로 관리 대상 개체에 대한 공유 권한이 필요하지 않은 한, unique_ptr배열 유형에 대한 부분 전문화 기능이 있기 때문에이 작업에 더 적합합니다.

std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]

라이브러리 기초를위한 C ++ 확장에 의해 도입 된 변경 사항

위에 열거 된 것들에 대한 또 다른 C ++ 17 이전 대안은 Library Fundamentals Technical Specification에 의해 제공되었으며 , 이는 shared_ptr객체 배열을 소유 한 경우에 대해 즉시 사용할 수 있도록 향상되었습니다. shared_ptr이 TS에 예정된 변경 사항 의 현재 초안은 N4082 에서 찾을 수 있습니다 . 이러한 변경 사항은 std::experimental네임 스페이스 를 통해 액세스 할 수 있으며 <experimental/memory>헤더에 포함됩니다 . shared_ptr배열 을 지원 하기 위해 관련된 몇 가지 변경 사항 은 다음과 같습니다.

— 멤버 유형의 정의가 element_type변경됨

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

— 회원 operator[]추가 중

 element_type& operator[](ptrdiff_t i) const noexcept;

- 달리 unique_ptr배열 부분 특수화 양 shared_ptr<T[]>shared_ptr<T[N]>유효한 것 둘 초래할 것이다 delete[]오브젝트 관리라는 배열된다.

 template<class Y> explicit shared_ptr(Y* p);

요구 사항 : Y완전한 유형이어야한다. 표현식이 delete[] pT, 어레이 형태, 또는 delete p경우 T배열 형없고, 잘 형성한다 잘 동작을 정의하며, 예외가 발생하지 않는다. 경우 T이다 U[N], Y(*)[N]로 변환한다 T*; 경우 T이다 U[], Y(*)[]로 변환한다 T*; 그렇지 않으면 Y*로 변환 할 수 T*있습니다.


9
+1, 비고 : Boost 's도 shared-array있습니다.
jogojapan

5
@ tshah06 shared_ptr::get은 관리 대상 개체에 대한 포인터를 반환합니다. 그래서 당신은 그것을 사용할 수 있습니다sp.get()[0] = 1; ... sp.get()[9] = 10;
Praetorian

55
ALT : en.cppreference.com/w/cpp/memory/default_deletestd::shared_ptr<int> sp( new int[10], std::default_delete<int[]>() ); 참조
yohjp

2
@Jeremy 컴파일 타임에 크기가 알려진 경우 클래스를 작성할 필요가 없으면 std::shared_ptr<std::array<int,N>>충분합니다.
Praetorian

13
unique_ptr부분 전문화를 얻지 만 shared_ptr그렇지 않습니까?
Adam

28

사용할 수있는 가장 쉬운 대안은 shared_ptr<vector<int>>입니다.


5
네 그렇습니다. 또는 벡터는 배열의 수퍼 세트입니다. 메모리 내 표현 (메타 데이터 포함)은 동일하지만 크기를 조정할 수 있습니다. 실제로 배열을 원하지만 벡터를 사용할 수없는 상황은 없습니다.
Timmmm

2
여기서의 차이점은 벡터 크기가 더 이상 정적이 아니며 데이터에 대한 액세스는 이중 간접적으로 수행된다는 것입니다. 성능이 중요한 문제가 아닌 경우이 방법이 작동합니다. 그렇지 않으면 어레이를 공유하는 데 자체 이유가있을 수 있습니다.
Emilio Garavaglia

4
그럼 당신은 아마 사용할 수 있습니다 shared_ptr<array<int, 6>>.
Timmmm

10
다른 차이점은 원시 배열보다 약간 더 크고 느리다는 것입니다. 일반적으로 문제는 아니지만 1 == 1.1 인 척하지 마십시오.
Andrew

2
배열의 데이터 소스가 벡터로 변환하기가 부적절하거나 불필요하다는 것을 의미하는 상황이 있습니다. 카메라에서 프레임을 가져올 때와 같이. (또는 어쨌든 그것은 내 이해입니다)
Narfanator
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.