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:
Bunun nedeni std::shared_ptr
, tür silmeyi uygularken , uygulamamasıdır std::unique_ptr
.
Yana std::shared_ptr
uygular-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_ptr
tü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_ptr
iki şeyi destekleyebilir:
void*
, ancak yine de yıkıcılarını doğru bir şekilde çağırarak yok edilen nesneleri düzgün bir şekilde silebilir .Peki. Bu tamamen nasıl std::shared_ptr
çalıştığıyla ilgili.
Şimdi soru, std::unique_ptr
nesneleri 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::function
silici türü olarak uygun olanı kullanarak başarabilirsinizunique_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::function
gerekli 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.
std::function
tü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.
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_target
Bö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>::lock
senkronize bir işlemdir.
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 getirebilirsinizD
.