Fark, std::make_sharedbir yığın ayırma std::shared_ptrgerçekleştirirken , yapıcı çağırmak iki gerçekleştirir.
Yığın ayırmaları nerede olur?
std::shared_ptr iki kuruluşu yönetir:
- kontrol bloğu (ref sayımları, tür silinmiş silme vb. gibi meta verileri depolar)
- yönetilen nesne
std::make_sharedhem kontrol bloğu hem de veri için gerekli alan için tek bir yığın ayırma muhasebesi gerçekleştirir. Diğer durumda, new Obj("foo")yönetilen veriler için bir yığın ayırmayı çağırır ve std::shared_ptryapıcı kontrol bloğu için başka bir yığın gerçekleştirir.
Daha fazla bilgi için cppreference adresindeki uygulama notlarına bakın .
Güncelleme I: İstisna-Güvenlik
NOT (2019/08/30) : C ++ 17'den bu yana, işlev bağımsız değişkenlerinin değerlendirme sırasındaki değişiklikler nedeniyle bu bir sorun değildir. Özellikle, bir işleve ait her bağımsız değişkenin, diğer bağımsız değişkenlerin değerlendirilmesinden önce tam olarak yürütülmesi gerekir.
OP olayların istisnai güvenlik tarafını merak ediyor gibi göründüğünden cevabımı güncelledim.
Bu örneği ele alalım,
void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),
std::shared_ptr<Rhs>(new Rhs("bar")));
C ++ alt ifadelerin rasgele değerlendirilmesine izin verdiğinden, olası bir sıralama şöyledir:
new Lhs("foo"))
new Rhs("bar"))
std::shared_ptr<Lhs>
std::shared_ptr<Rhs>
Şimdi, 2. adımda bir istisna aldığımızı varsayın (örneğin, bellek dışı istisna, Rhskurucu bir istisna attı). Daha sonra 1. adımda ayrılan belleği kaybediyoruz, çünkü hiçbir şeyin onu temizleme şansı olmayacaktı. Buradaki sorunun özü, ham işaretçinin std::shared_ptrderhal kurucuya geçmediğidir .
Bunu düzeltmenin bir yolu, bunları ayrı satırlarda yapmaktır, böylece bu arbiter sıralama gerçekleşemez.
auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);
Bunu çözmenin tercih edilen yolu elbette kullanmaktır std::make_shared.
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
Güncelleme II: Dezavantajı std::make_shared
Casey'nin yorumundan alıntı :
Yalnızca tek bir ayırma olduğundan, kontrol bloğu artık kullanılmayana kadar sınıfa ait bellek yer değiştirilemez. A weak_ptrkontrol bloğunu süresiz olarak hayatta tutabilir.
Neden weak_ptrs örnekleri kontrol bloğunu canlı tutuyor?
S'nin weak_ptryönetilen nesnenin hala geçerli olup olmadığını belirlemesinin bir yolu olmalıdır (örn. İçin lock). Bunu shared_ptr, kontrol bloğunda saklanan yönetilen nesneye sahip olan s sayısını kontrol ederek yaparlar . Sonuç, kontrol bloklarının shared_ptrsayım ve weak_ptrsayımın her ikisi 0'a ulaşana kadar hayatta kalmasıdır .
Sayfasına geri dön std::make_shared
Yana std::make_sharedkontrol bloğu ve yönetilen nesne için tek yığın ayırma yapan, kontrol bloğu için bellek ve bağımsız olarak yönetilen nesne serbest bir yolu yoktur. Biz kontrol bloğunu ve hiçbir kalmayıncaya kadar olur yönetilen nesne, hem özgür kadar beklemek zorundayız shared_ptrler veya weak_ptryaşıyor.
Bunun yerine, kontrol bloğu ve yönetilen nesne newve shared_ptryapıcı için iki yığın ayırma gerçekleştirdiğimizi varsayalım . Daha sonra shared_ptrcanlı olmadığında yönetilen nesnenin hafızasını (belki daha erken) boşaltırız ve canlı olmadığında kontrol bloğunun hafızasını (belki daha sonra) weak_ptrboşaltırız.