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_ptrbu 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_ptraynı nesneyi temizlemenin yan etkisi olan bir şeyi çağırırsanız ? Ya shared_ptro 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::stringbirden fazla yere aktarılırken kopyalanması muhtemel bir mesaj kullanmak yerine , a'dan shared_ptrbir 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_messagea 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_messagesorunun 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 msgasla 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_ptrsürekli olarak boş kalmaya devam edebilmesini istediğinizde kolay çözüm, fonksiyonun shared_ptrmevcut 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_ptrdö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::stringAş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.