Farklı bir shared_ptr
örneğin amacı shared_ptr
, kapsam dahilinde olduğu sürece , işaret ettiği nesnenin hala var olacağını garanti etmektir (mümkün olduğunca) , çünkü referans sayısı en az 1 olacaktır.
Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
// sp points to an object that cannot be destroyed during this function
}
Yani a'ya bir referans kullanarak shared_ptr
bu garantiyi devre dışı bırakırsınız. Öyleyse ikinci durumunuzda:
Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here
{
...
sp->do_something();
...
}
sp->do_something()
Boş işaretçi nedeniyle patlamayacağını nasıl biliyorsun ?
Her şey kodun bu '...' bölümlerinde ne olduğuna bağlı. Ya ilk '...' sırasında (kodun başka bir bölümünde bir yerde) shared_ptr
aynı nesneyi temizlemenin yan etkisi olan bir şeyi çağırırsanız ? Ya shared_ptr
o nesneden geriye kalan tek fark bu olursa ? Güle güle nesnesi, tam da denemek ve kullanmak üzere olduğunuz yerde.
Yani bu soruyu cevaplamanın iki yolu var:
Tüm programınızın kaynağını, nesnenin işlev gövdesi sırasında ölmeyeceğinden emin olana kadar çok dikkatli bir şekilde inceleyin.
Parametreyi referans yerine farklı bir nesne olacak şekilde değiştirin.
Burada geçerli olan genel bir tavsiye: performans uğruna kodunuzda riskli değişiklikler yapma zahmetine girmeyin; ürününüzü bir profilleyicide gerçekçi bir durumda zamanlayana ve yapmak istediğiniz değişikliğin bir etki yaratacağını kesin olarak ölçene kadar performans açısından önemli bir fark.
Yorumcu JQ için güncelleme
İşte uydurma bir örnek. Kasıtlı olarak basittir, bu nedenle hata açık olacaktır. Gerçek örneklerde hata o kadar açık değildir çünkü gerçek ayrıntı katmanlarında gizlidir.
Bir yere mesaj gönderecek bir fonksiyonumuz var. Büyük bir mesaj olabilir, bu nedenle std::string
birden fazla yere aktarılırken kopyalanması muhtemel bir mesaj kullanmak yerine , a'dan shared_ptr
bir dizeye kullanırız:
void send_message(std::shared_ptr<std::string> msg)
{
std::cout << (*msg.get()) << std::endl;
}
(Bu örnek için sadece konsola "gönderiyoruz").
Şimdi bir önceki mesajı hatırlamak için bir tesis eklemek istiyoruz. Aşağıdaki davranışı istiyoruz: En son gönderilen mesajı içeren bir değişken bulunmalıdır, ancak şu anda bir mesaj gönderilirken önceki mesaj olmamalıdır (değişken gönderilmeden önce sıfırlanmalıdır). Bu yüzden yeni değişkeni açıklıyoruz:
std::shared_ptr<std::string> previous_message;
Ardından belirlediğimiz kurallara göre işlevimizi değiştiriyoruz:
void send_message(std::shared_ptr<std::string> msg)
{
previous_message = 0;
std::cout << *msg << std::endl;
previous_message = msg;
}
Bu nedenle, göndermeye başlamadan önce mevcut önceki mesajı atıyoruz ve ardından gönderme tamamlandıktan sonra yeni önceki mesajı saklayabiliriz. Hepsi iyi. İşte bazı test kodları:
send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);
Ve beklendiği gibi, bu Hi!
iki kez yazdırılır .
Şimdi, koda bakan ve şöyle düşünen Bay Bakımcı geliyor: Hey, bu parametre send_message
a shared_ptr
:
void send_message(std::shared_ptr<std::string> msg)
Açıkçası şu şekilde değiştirilebilir:
void send_message(const std::shared_ptr<std::string> &msg)
Bunun getireceği performans geliştirmeyi bir düşünün! (Bazı kanallar üzerinden tipik olarak büyük bir mesaj göndermek üzere olduğumuzu aklınızdan çıkarmayın, bu nedenle performans artışı ölçülemeyecek kadar küçük olacaktır).
Ancak asıl sorun, test kodunun artık tanımlanmamış davranış sergileyecek olmasıdır (Visual C ++ 2010 hata ayıklama yapılarında çöküyor).
Bay Maintainer buna şaşırır, ancak send_message
sorunun oluşmasını engellemek için bir savunma kontrolü ekler :
void send_message(const std::shared_ptr<std::string> &msg)
{
if (msg == 0)
return;
Ama tabii ki hala devam ediyor ve çöküyor, çünkü çağrıldığında msg
asla boş değil send_message
.
Dediğim gibi, önemsiz bir örnekte tüm kodlar birbirine çok yakın olduğundan, hatayı bulmak kolay. Ancak gerçek programlarda, değişken nesneler arasında daha karmaşık ilişkiler bulunan ve birbirlerini gösteren işaretçiler olduğu için , hatayı yapmak kolaydır ve hatayı tespit etmek için gerekli test senaryolarını oluşturmak zordur.
Bir fonksiyonun shared_ptr
sürekli olarak boş kalmaya devam edebilmesini istediğinizde kolay çözüm, fonksiyonun shared_ptr
mevcut bir referansa güvenmek yerine kendi gerçek değerini tahsis etmesidir shared_ptr
.
Dezavantajı ise, kopyalamanın shared_ptr
ücretsiz olmamasıdır: "kilitsiz" uygulamalar bile iş parçacığı garantilerini yerine getirmek için birbirine kenetlenmiş bir işlem kullanmak zorundadır. Dolayısıyla, bir programın a'yı a'ya shared_ptr
dönüştürerek önemli ölçüde hızlandırılabileceği durumlar olabilir shared_ptr &
. Ancak bu, tüm programlara güvenle yapılabilecek bir değişiklik değildir. Programın mantıksal anlamını değiştirir.
std::string
Aşağıdakiler yerine std::shared_ptr<std::string>
ve yerine kullanırsak benzer bir hatanın ortaya çıkacağını unutmayın :
previous_message = 0;
mesajı temizlemek için dedik:
previous_message.clear();
O zaman belirti, tanımlanmamış davranış yerine yanlışlıkla boş bir mesajın gönderilmesi olabilir. Çok büyük bir dizenin fazladan bir kopyasının maliyeti, a kopyalama maliyetinden çok daha önemli olabilir shared_ptr
, bu nedenle takas farklı olabilir.