Özel bir ayırıcı tarafından ayrılmış bellekte paylaşılan_ptr silme işlemi mi saklanıyor?


22

shared_ptrÖzel bir ayırıcıya ve özel bir siliciye sahip bir diyelim .

Ben deleter saklanmalıdır nereye bahsediyor, standartta bir şey bulamıyorum: o özel ayırıcısı Deleter hafızasına için kullanılacak demiyor ve bunu söylemiyor olmaz olsun.

Bu belirtilmemiş mi yoksa sadece bir şey mi kaçırıyorum?

Yanıtlar:


11

C ++ 11'de util.smartptr.shared.const / 9:

Efektler: p nesnesine ve d sileceğine sahip olan bir paylaşılan_ptr nesnesi oluşturur. İkinci ve dördüncü kurucular dahili kullanım için bellek ayırmak üzere a'nın bir kopyasını kullanacaktır.

İkinci ve dördüncü kurucular şu prototiplere sahiptir:

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);

Son taslakta, util.smartptr.shared.const / 10 bizim amacımızla eşdeğerdir:

Efektler: p nesnesine ve d sileceğine sahip olan bir paylaşılan_ptr nesnesi oluşturur. T bir dizi türü olmadığında, birinci ve ikinci kurucular p ile paylaşılan_from_bu işlevini etkinleştirir. İkinci ve dördüncü kurucular dahili kullanım için bellek ayırmak üzere a'nın bir kopyasını kullanacaktır. Bir istisna atılırsa, d (p) çağrılır.

Dolayısıyla, tahsis edilen belleğe tahsis edilmesi gerekiyorsa, ayırıcı kullanılır. Mevcut standarda ve ilgili hata raporlarına dayanarak, tahsis zorunlu değildir ancak komite tarafından üstlenilir.

  • Arayüz rağmen shared_ptrorada hiç bir kontrol bloğu ve hepsi bir uygulama verir shared_ptrve weak_ptrbağlantılı bir listeye konur, uygulamada böyle bir uygulama yoktur. Ayrıca, ifadeler, örneğin use_countbunun paylaşıldığı varsayılarak değiştirilmiştir .

  • Silinmenin yalnızca yapılandırılabilir hareket etmesi gerekir. Böylece, içinde birkaç kopya olması mümkün değildir shared_ptr.

Silmeyi özel olarak tasarlanmış bir yere koyan shared_ptrve özel shared_ptrsilindiğinde hareket ettiren bir uygulama düşünülebilir . Uygulama uygun görünse de, özellikle kullanım sayısı için bir kontrol bloğuna ihtiyaç duyulabileceği için gariptir (belki de kullanım sayısıyla aynı şeyi yapmak garip olabilir).

Bulduğum ilgili DR'ler: 545 , 575 , 2434 (bu, tüm uygulamaların bir kontrol bloğu kullandığını kabul eder ve çoklu iş parçacığı kısıtlamalarının biraz zorunlu kıldığını ima eder), 2802 (bu, silmenin yalnızca yapılandırılabilir hareket etmesini gerektirir ve böylece uygulamayı silme işlemi birkaç shared_ptr's arasında kopyalanır ).


2
"dahili kullanım için bellek ayırmak için" Uygulama dahili kullanım için bellek ayırmayacaksa ne olur? Bir üye kullanabilir.
LF

1
@LF Yapamaz, arayüz buna izin vermez.
AProgrammer

Teorik olarak, hala bir tür "küçük silme optimizasyonu" kullanabilir, değil mi?
LF

Ne garip ben (kopyasını aynı ayırıcısı kullanarak hakkında bir şey bulamıyorum ki akadar) ayırması o bellek. Bu, kopyasının bir miktar depolanmasını gerektirir a. [Util.smartptr.shared.dest] içinde bilgi yoktur.
Daniel Langr

1
@DanielsaysreinstateMonica, util.smartptr.shared / 1 içinde "1. veya saklanan işaretçiyle ilişkilendirilmiş kaynakları serbest bırakabilirsiniz. " depolanan işaretleyici ile ilişkili serbest kaynakları bunun için uygun değildir. Ancak kontrol bloğu, son zayıf işaretçi silinene kadar hayatta kalmalıdır.
AProgrammer

4

Gönderen std :: shared_ptr Elimizdeki:

Kontrol bloğu, aşağıdakileri içeren dinamik olarak ayrılmış bir nesnedir:

  • yönetilen nesneye bir işaretçi veya yönetilen nesnenin kendisi;
  • silme (tip silme);
  • ayırıcı (tip silinmiş);
  • yönetilen nesneye sahip olan paylaşılan_ptrlerin sayısı;
  • yönetilen nesneye başvuran zayıf_ptrs sayısı.

Ve std :: tahsis_shared gelen :

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

T türünde bir nesne oluşturur ve paylaşılan işaretçinin kontrol bloğu ve T nesnesi için bir ayırma kullanmak üzere std :: shared_ptr [...] içine sarar .

Yani std :: tahsis_shared, deleterile ayırmak gibi görünüyor Alloc.

EDIT: Ve n4810§20.11.3.6 Oluşturma [util.smartptr.shared.create]

Tümü için geçerlidir 1 ortak gereksinimleri make_shared, allocate_shared, make_shared_default_initve allocate_shared_default_initaksi belirtilmediği sürece fazla yüklenmeleri, aşağıda anlatılmıştır.

[...]

Notlar: (7.1) - Uygulamalar, birden fazla bellek ayırma gerçekleştirmemelidir. [Not: Bu, müdahaleci bir akıllı işaretçiye eşdeğer verimlilik sağlar. —End not]

[Vurgu benim)

Standart bunun kontrol bloğu için kullanılması std::allocate_shared gerektiğini söylüyor Alloc.


1
Cppreference tarafından normatif bir metin olmadığım için üzgünüm. Bu harika bir kaynak, ancak dil-avukat soruları için değil .
StoryTeller - Unslander Monica

@ StoryTeller-UnslanderMonica Tamamen katılıyorum - en son standarda baktı ve bu yüzden cppreference ile gitti bir şey bulamadı.
Paul Evans


n4810Yanıt bulundu ve güncellendi.
Paul Evans

1
Ancak bu, make_sharedinşaatçıların kendilerinden değil. Yine de küçük üyeler için üye kullanabilirim.
LF

3

Bunun belirtilmediğine inanıyorum.

İlgili kurucuların özellikleri: [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);

Efektler:shared_­ptr Nesneye pve silmeye sahip olan bir nesne oluşturur d. Ne zaman Tbir dizi türü değil, birinci ve ikinci kurucular olanak shared_­from_­thisile p. İkinci ve dördüncü kurucular dahili kullanım için bellek tahsis etmek üzere bir kopyasını akullanacaktır . Bir istisna atılırsa d(p)çağrılır.

Şimdi benim yorumum, uygulama dahili kullanım için belleğe ihtiyaç duyduğunda bunu kullanarak yapmaktır a. Bu, uygulamanın her şeyi yerleştirmek için bu belleği kullanması gerektiği anlamına gelmez. Örneğin, şu garip bir uygulama olduğunu varsayın:

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
    }
// ...
};

Bu uygulama " adahili kullanım için bellek ayırmak için bir kopyası kullanıyor mu?" Evet öyle. Kullanmak dışında hiçbir zaman bellek ayırmaz a. Bu naif uygulamada birçok sorun var, ancak diyelim ki, shared_ptrdoğrudan bir işaretleyiciden inşa edilen ve asla kopyalanmayan veya taşınmayan veya başka şekilde referans edilmeyen ve başka hiçbir komplikasyon bulunmayan en basit durum hariç tüm ayırıcıları kullanmaya geçiyor . Mesele şu ki, geçerli bir uygulamanın hayal edemediğimiz için, teorik olarak var olamayacağını kanıtlamıyor. Böyle bir uygulamanın gerçek dünyada bulunabileceğini söylemiyorum, sadece standart aktif olarak yasaklıyor gibi görünmüyor.


IMO sizin shared_ptriçin küçük tipler yığın bellek ayırır. Ve böylece standart gereksinimleri karşılamıyor
bartop

1
@bartop Yığına herhangi bir bellek ayırmaz. _Smaller_deleter, koşulsuz olarak paylaşılan_ptr ifadesinin bir parçasıdır. Bu alanda bir kurucu çağırmak hiçbir şey tahsis etmek anlamına gelmez. Aksi takdirde, kontrol bloğuna bir işaretçi tutmak bile “bellek ayırma” olarak sayılır, değil mi? :-)
LF

Ancak silicinin kopyalanabilir olması gerekmez, bu nasıl olur?
Nicol Bolas

@NicolBolas Umm ... Kullanın std::move(__d)ve allocatekopyalama gerektiğinde geri dönün .
LF
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.