shared_ptr의 삭제 기는 커스텀 할당자가 할당 한 메모리에 저장되어 있습니까?


22

shared_ptr사용자 지정 할당 자 사용자 지정 삭제 기가 있다고 가정 해보십시오.

표준에서 삭제 프로그램을 저장할 위치에 대한 내용을 찾을 수 없습니다. 사용자 지정 할당자가 삭제 프로그램의 메모리에 사용될 것이라고 말하지 않으며 그렇지 않을 것이라고 말하지 않습니다 .

이것이 지정되지 않았습니까? 아니면 뭔가 빠졌습니까?

답변:


11

C ++ 11의 util.smartptr.shared.const / 9 :

효과 : 객체 p와 삭제 기 d를 소유 한 shared_ptr 객체를 구성합니다. 두 번째와 네 번째 생성자는 내부 용으로 메모리를 할당하기 위해 a의 사본을 사용해야합니다.

두 번째와 네 번째 생성자에는 다음과 같은 프로토 타입이 있습니다.

template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

최신 초안에서 util.smartptr.shared.const / 10은 다음과 같은 목적으로 사용됩니다.

효과 : 객체 p와 삭제 기 d를 소유 한 shared_ptr 객체를 구성합니다. T가 배열 유형이 아닌 경우 첫 번째 및 두 번째 생성자는 p와 shared_from_this를 활성화합니다. 두 번째와 네 번째 생성자는 내부 용으로 메모리를 할당하기 위해 a의 사본을 사용해야합니다. 예외가 발생하면 d (p)가 호출됩니다.

따라서 할당 된 메모리에 할당해야 할 경우 할당자가 사용됩니다. 현재 표준 및 관련 결함 보고서를 기반으로, 할당은 필수가 아니라위원회가 가정합니다.

  • 의 인터페이스가 있지만 shared_ptr결코 제어 블록 및 모든 없습니다 구현 할 수 shared_ptrweak_ptr링크 된 목록에 넣어가, 실제로 이러한 구현은 없다. 또한, 예를 들어, 문구 use_count가 공유 되는 것으로 가정하여 문구가 수정되었습니다 .

  • 삭제자는 구성 가능한 이동 만 필요합니다. 따라서에 여러 사본을 보유 할 수 없습니다 shared_ptr.

삭제 기를 특수하게 설계 shared_ptr하여 shared_ptr삭제 하고 스페셜 이 삭제 될 때 이동 하는 구현을 상상할 수 있습니다 . 구현이 준수하는 것처럼 보이지만 특히 사용 횟수에 제어 블록이 필요할 수 있기 때문에 이상합니다.

내가 찾은 관련 예탁 증서 : 545 , 575 , 2434 , (모든 구현이 제어 블록을 사용하는 것을 인정 다소을 의무화하는 멀티 스레딩 제약을 의미하는 것) 2802 Deleter가 만 작도 이동해야하는 (따라서 구현 어디 방지 삭제는 여러 개의 사이에 복사됩니다 shared_ptr).


2
"내부 용으로 메모리를 할당하려면"구현에서 내부 용으로 메모리를 할당하지 않으면 어떻게됩니까? 멤버를 사용할 수 있습니다.
LF

1
@LF 인터페이스가 허용하지 않습니다.
AProgrammer

이론적으로는 여전히 "작은 삭제 프로그램 최적화"를 사용할 수 있습니까?
LF

이상한 점은 동일한 할당 자 (의 사본 a)를 사용하여 해당 메모리 를 할당 해제 하는 것에 대해 아무것도 찾을 수 없다는 것 입니다. 이 사본의 일부 저장 공간을 의미 a합니다. [util.smartptr.shared.dest]에는 정보가 없습니다.
Daniel Langr

1
@DanielsaysreinstateMonica, util.smartptr.shared / 1에서 다음과 같은지 궁금합니다. "shared_ptr 클래스 템플릿은 일반적으로 new.shared_ptr을 통해 얻은 포인터를 저장합니다. shared_ptr은 공유 소유권의 의미를 구현합니다. 그렇지 않으면 저장된 포인터와 관련된 리소스를 해제합니다. " 저장된 포인터와 관련된 방출 자원은 그위한 것이 아닙니다. 그러나 마지막 약한 포인터가 삭제 될 때까지 제어 블록도 그대로 유지됩니다.
AProgrammer

4

에서 표준 : : shared_ptr의 우리가 있습니다 :

제어 블록은 다음을 보유한 동적으로 할당 된 객체입니다.

  • 관리 객체 또는 관리 객체 자체에 대한 포인터;
  • 삭제 기 (유형 소거);
  • 할당 자 (유형 소거);
  • 관리 객체를 소유 한 shared_ptr의 수;
  • 관리 객체를 나타내는 weak_ptr의 수

그리고 std :: allocate_shared에서 다음 을 얻습니다.

template< class T, class Alloc, class... Args >
shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );

공유 포인터 의 제어 블록 과 T 개체 모두에 대해 하나의 할당 을 사용하기 위해 T 형식의 개체를 구성하고 std :: shared_ptr [...]에 래핑 합니다.

그래서처럼 보이는 표준 : allocate_shared 를 할당해야 deleter당신과 함께 Alloc.

편집 : 그리고 n4810§20.11.3.6 생성 [util.smartptr.shared.create]

모두에 적용되는 1 개 공통 요구 사항은 make_shared, allocate_shared, make_shared_default_init,와 allocate_shared_default_init달리 명시되지 않는 한 과부하, 아래에 설명되어 있습니다.

[...]

7 비고 : (7.1) — 구현은 하나 이상의 메모리 할당을 수행하지 않아야합니다. [참고 : 이것은 침입 스마트 포인터와 동등한 효율성을 제공합니다. — 끝 참고]

[모든 것의 헛소리]

표준 그래서 말하는 std::allocate_shared 해야 사용 Alloc제어 블록.


1
나는 cppreference가 표준 텍스트가 아니라는 것에 대해 유감입니다. 훌륭한 자료이지만 반드시 언어 변호사 질문에 필요한 것은 아닙니다 .
StoryTeller-Unslander Monica

@ StoryTeller-UnslanderMonica 완전히 동의합니다-최신 표준을 살펴본 결과 cppreference와 함께 아무것도 찾을 수 없었습니다.
Paul Evans


n4810답을 찾고 업데이트했습니다.
Paul Evans

1
그러나 이것은 make_shared생성자 자체가 아니라 에 대해 이야기 하고 있습니다. 여전히 작은 삭제 프로그램에 멤버를 사용할 수 있습니다.
LF

3

나는 이것이 불특정이라고 믿는다.

관련 생성자의 사양은 다음과 같습니다. [util.smartptr.shared.const] / 10

template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);

효과 : 구축 shared_­ptr객체 소유 객체 p와 Deleter가를 d. 되면 T배열 유형이 아닌, 제 1 및 제 2 활성화 생성자 shared_­from_­thisp. 두 번째와 네 번째 생성자 a내부 용으로 메모리를 할당 하기 위해 사본을 사용해야 합니다. 예외가 발생하면 d(p)이 호출됩니다.

이제 내 구현에는 내부 사용을 위해 메모리가 필요할 때을 사용하여 수행된다는 해석이 a있습니다. 구현에서이 메모리를 사용하여 모든 것을 배치해야한다는 의미는 아닙니다. 예를 들어, 이상한 구현이 있다고 가정하십시오.

template <typename T>
class shared_ptr : /* ... */ {
    // ...
    std::aligned_storage<16> _Small_deleter;
    // ...
public:
    // ...
    template <class _D, class _A>
    shared_ptr(nullptr_t, _D __d, _A __a) // for example
        : _Allocator_base{__a}
    {
        if constexpr (sizeof(_D) <= 16)
            _Construct_at(&_Small_deleter, std::move(__d));
        else
            // use 'a' to allocate storage for the deleter
    }
// ...
};

이 구현이 "사본을 a위해 메모리를 할당 하기 위해 사본을 사용합니까?" 그렇습니다. 를 사용하지 않고는 메모리를 할당하지 않습니다 a. 이 순진한 구현에는 많은 문제가 있지만 shared_ptr포인터에서 직접 생성되고 결코 복사되거나 이동되거나 참조되지 않으며 다른 합병증이없는 가장 단순한 경우를 제외하고 할당자를 사용하도록 전환한다고 가정 해 봅시다 . 요점은, 우리가 유효한 구현이 그 자체로는 이론적으로 존재할 수 없다는 것을 증명하지 못한다고 상상하지 못하기 때문입니다. 나는 실제로 그러한 구현이 실제 세계에서 발견 될 수 있다고 말하지는 않으며 표준이 적극적으로 그것을 금지하지 않는 것 같습니다.


shared_ptr작은 유형의 IMO 는 스택에 메모리를 할당합니다. 표준 요구 사항도 충족하지 못함
bartop

1
@bartop 스택의 메모리를 "할당"하지 않습니다. _Smaller_deleter는 무조건 shared_ptr 표현의 일부입니다. 이 공간에서 생성자를 호출한다고해서 아무것도 할당하는 것은 아닙니다. 그렇지 않으면, 제어 블록에 대한 포인터를 보유하더라도 "할당 메모리"로 간주됩니다. :-)
LF

그러나 삭제 기는 복사 할 필요가 없으므로 어떻게 작동합니까?
Nicol Bolas

@NicolBolas Umm ...을 사용 std::move(__d)하고 allocate복사가 필요할 때 되돌아갑니다 .
LF
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.