Fark, std::make_shared
bir yığın ayırma std::shared_ptr
gerç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_shared
hem 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_ptr
yapı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, Rhs
kurucu 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_ptr
derhal 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_ptr
kontrol bloğunu süresiz olarak hayatta tutabilir.
Neden weak_ptr
s örnekleri kontrol bloğunu canlı tutuyor?
S'nin weak_ptr
yö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_ptr
sayım ve weak_ptr
sayımın her ikisi 0'a ulaşana kadar hayatta kalmasıdır .
Sayfasına geri dön std::make_shared
Yana std::make_shared
kontrol 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_ptr
ler veya weak_ptr
yaşıyor.
Bunun yerine, kontrol bloğu ve yönetilen nesne new
ve shared_ptr
yapıcı için iki yığın ayırma gerçekleştirdiğimizi varsayalım . Daha sonra shared_ptr
canlı 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_ptr
boşaltırız.