Neden shared_ptr <void> yasal, benzersiz_ptr <void> ise bozuk?


103

Soru gerçekten başlığa uyuyor: Bu farkın teknik nedeninin ne olduğunu merak ediyorum, aynı zamanda mantığı da var mı?

std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;

Yanıtlar:


123

Bunun nedeni std::shared_ptr, tür silmeyi uygularken , uygulamamasıdır std::unique_ptr.


Yana std::shared_ptruygular-silinti tip, aynı zamanda destekleyen başka , viz ilginç özelliği. o mu değil deleter türünü ihtiyaç şablon türü argüman olarak sınıf şablonuna. Beyanlarına bakın:

template<class T,class Deleter = std::default_delete<T> > 
class unique_ptr;

ki vardır Deleter, tip parametre olarak

template<class T> 
class shared_ptr;

ona sahip değil.

Şimdi soru şu, neden shared_ptrtür silmeyi uyguluyor? Peki bu destek referansı sayma zorundadır ve bunu desteklemek için, bu yığınından bellek ayırmaya çünkü, bu yüzden yapar ve o zamandan beri vardır zaten bellek ayrılamadı, bu daha fazla ve uygular tip-silme bir adım gider - hangi yığın ihtiyacı tahsis de. Yani temelde sadece oportünist olmak!

Tür silme nedeniyle std::shared_ptriki şeyi destekleyebilir:

  • Her türden nesneyi depolayabilir void*, ancak yine de yıkıcılarını doğru bir şekilde çağırarak yok edilen nesneleri düzgün bir şekilde silebilir .
  • Silici türü, sınıf şablonuna tür bağımsız değişkeni olarak aktarılmaz, bu da tür güvenliğinden ödün vermeden biraz özgürlük anlamına gelir .

Peki. Bu tamamen nasıl std::shared_ptrçalıştığıyla ilgili.

Şimdi soru, std::unique_ptrnesneleri olarak saklayabilir void*mi? Cevap, evet - argüman olarak uygun bir silmeyi geçmeniz şartıyla. İşte böyle bir gösteri:

int main()
{
    auto deleter = [](void const * data ) {
        int const * p = static_cast<int const*>(data);
        std::cout << *p << " located at " << p <<  " is being deleted";
        delete p;
    };

    std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);

} //p will be deleted here, both p ;-)

Çıktı ( çevrimiçi demo ):

959 located at 0x18aec20 is being deleted

Yorumda çok ilginç bir soru sordunuz:

Benim durumumda bir tür silme silicisine ihtiyacım olacak, ancak bu da mümkün görünüyor (bir miktar yığın tahsisi pahasına). Temel olarak, bu 3. tür bir akıllı işaretçi için bir niş nokta olduğu anlamına mı geliyor: tür silme özelliğine sahip özel bir sahiplik akıllı işaretçi.

hangi @Steve Jessop aşağıdaki çözümü önerdi

Bunu aslında hiç denemedim, ama belki bunu std::functionsilici türü olarak uygun olanı kullanarak başarabilirsiniz unique_ptr? Bunun gerçekten işe yaradığını varsayarsak, işiniz bitti, münhasır sahiplik ve türü silinmiş bir silici.

Bu öneriyi takiben, bunu uyguladım ( std::functiongerekli görünmediği için kullanılmasa da):

using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;

template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
    return unique_void_ptr(ptr, [](void const * data) {
         T const * p = static_cast<T const*>(data);
         std::cout << "{" << *p << "} located at [" << p <<  "] is being deleted.\n";
         delete p;
    });
}

int main()
{
    auto p1 = unique_void(new int(959));
    auto p2 = unique_void(new double(595.5));
    auto p3 = unique_void(new std::string("Hello World"));
}  

Çıktı ( çevrimiçi demo ):

{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.

Umarım yardımcı olur.


13
Güzel cevap, +1. Ancak std::unique_ptr<void, D>, bir uygun sağlayarak a'nın hala mümkün olduğunu açıkça belirterek bunu daha da iyi hale getirebilirsiniz D.
Angew artık SO

1
@Angrew: Güzel bir soru, sorumda yazılmayan asıl soruyu buldunuz;)
Ad N

@Nawaz: Teşekkür ederim. Benim durumumda bir tür silme silicisine ihtiyacım olacak, ancak bu da mümkün görünüyor (bir miktar yığın tahsisi pahasına). Temel olarak, bu 3. tür bir akıllı işaretçi için bir niş nokta olduğu anlamına mı geliyor: tür silme özelliğine sahip özel bir sahiplik akıllı işaretçi?
Reklam N

8
@AdN: Bunu aslında hiç denemedim, ama belki bunu silici std::functiontürü olarak uygun olanı kullanarak başarabilirsin unique_ptr? Bunun gerçekten işe yaradığını varsayarsak, işiniz bitti, münhasır sahiplik ve türü silinmiş bir silici.
Steve Jessop

Gramer nit: "neden X fiilleri Y?" olmalıdır "neden yapar X fiil Y?" İngilizcede.
zwol

7

Gerekçelerden biri, a'nın birçok kullanım durumundan biridir shared_ptr- yani bir ömür göstergesi veya gözcü olarak.

Orijinal destek belgelerinde bundan bahsedilmişti:

auto register_callback(std::function<void()> closure, std::shared_ptr<void> pv)
{
    auto closure_target = { closure, std::weak_ptr<void>(pv) };
    ...
    // store the target somewhere, and later....
}

void call_closure(closure_target target)
{
    // test whether target of the closure still exists
    auto lock = target.sentinel.lock();
    if (lock) {
        // if so, call the closure
        target.closure();
    }
}

closure_targetBöyle bir şey nerede :

struct closure_target {
    std::function<void()> closure;
    std::weak_ptr<void> sentinel;
};

Arayan, şuna benzer bir geri arama kaydeder:

struct active_object : std::enable_shared_from_this<active_object>
{
    void start() {
      event_emitter_.register_callback([this] { this->on_callback(); }, 
                                       shared_from_this());
    }

    void on_callback()
    {
        // this is only ever called if we still exist 
    }
};

çünkü shared_ptr<X>her zaman dönüştürülebilir olduğu için shared_ptr<void>, event_emitter artık geri çağırdığı nesnenin türünün farkında olmayabilir.

Bu düzenleme, aboneleri geçiş durumlarını işleme yükümlülüğünün olay yayıcısına bırakır (ya bir kuyruktaki geri arama, active_object ortadan kalkarken eyleme geçmeyi beklerse?) Ve ayrıca abonelik iptalinin senkronize edilmesine gerek olmadığı anlamına gelir. weak_ptr<void>::locksenkronize bir işlemdir.

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.