C ++ - başvuruları std :: shared_ptr veya boost :: shared_ptr'ye iletme


115

A ile çalışması gereken bir işleve shared_ptrsahipsem, ona bir referans iletmek ( shared_ptrnesneyi kopyalamaktan kaçınmak için) daha verimli olmaz mıydı ? Olası kötü yan etkiler nelerdir? İki olası durum öngörüyorum:

1) işlevin içinde argümanın bir kopyası yapılır, örneğin

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2) işlevin içinde argüman yalnızca şu şekilde kullanılır:

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

Her iki durumda da boost::shared_ptr<foo>referans yerine değere göre geçmek için iyi bir neden göremiyorum . Değere göre geçiş, kopyalama nedeniyle referans sayısını yalnızca "geçici olarak" artırır ve ardından işlev kapsamından çıkarken azaltır. Bir şeyi gözden mi kaçırıyorum?

Birkaç cevabı okuduktan sonra açıklığa kavuşturmak için: Erken optimizasyon endişeleri konusunda tamamen aynı fikirdeyim ve her zaman önce profil oluşturmaya, sonra da sıcak noktalarda çalışmaya çalışırım. Ne demek istediğimi anladıysanız, sorum tamamen teknik bir kod bakış açısıydı.


Sorunuzun etiketlerini değiştirip değiştiremeyeceğinizi bilmiyorum, ancak lütfen oraya bir destek etiketi eklemeyi deneyin. Bu soruyu aradım ama bulamadım çünkü boost ve smart-pointer etiketlerini aradım. Bu yüzden sorunuzu kendi sorumu oluşturduktan hemen sonra buldum
Edison Gustavo Muenz 06

Yanıtlar:


113

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:

  1. 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.

  2. 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.


16
Aktarılan paylaşılan_tr, arama sitesinde zaten bir kapsamda yaşar. Bu sorudaki kodun sarkan bir işaretçi nedeniyle patlayacağı ayrıntılı bir senaryo oluşturabilirsiniz, ancak o zaman referans parametresinden daha büyük sorunlarınız olduğunu varsayıyorum!
Magnus Hoff

10
Bir üyede saklanabilir. O üyeyi temizlemek için olan bir şeyi arayabilirsin. Smart_ptr'nin tüm amacı, yaşam sürelerini çağrı yığını etrafında yuvalanan hiyerarşiler veya kapsamlarda koordine etmek zorunda kalmamaktır, bu nedenle yaşam sürelerinin bu tür programlarda bunu yapmadığını varsaymak en iyisidir.
Daniel Earwicker

7
Aslında benim bakış açım değil! Söylediğimin kodumla ilgili özel bir şey olduğunu düşünüyorsanız, beni anlamamış olabilirsiniz. İlk etapta shared_ptr'nin var olmasının kaçınılmaz bir sonucundan bahsediyorum: birçok nesne yaşam süresi sadece işlev çağrılarıyla ilgili değildir.
Daniel Earwicker

8
@DanielEarwicker tüm puanlarınıza tamamen katılıyor ve muhalefet seviyesi beni şaşırttı. Endişelerinizi daha da alakalı kılan bir şey, işin içine girdiği zaman, bir nesnenin geçerliliği çok daha önemli hale geldiğinde, bir nesnenin garantisi çok daha önemli hale gelir. İyi cevap.
radman

3
Kısa bir süre önce, paylaşılan bir işaretçiye referansta bulunulmasından kaynaklanan çok ciddi bir hatayı takip ettim. Kod, bir nesnenin durum değişikliğini işliyordu ve nesnenin durumunun değiştiğini fark ettiğinde, onu önceki durumdaki nesneler koleksiyonundan çıkardı ve yeni durumdaki nesneler koleksiyonuna taşıdı. Kaldırma işlemi, nesneye yönelik son paylaşılan işaretçiyi yok etti. Üye işlevi, koleksiyondaki paylaşılan işaretçiye bir başvuru üzerine çağrılmıştı. Boom. Daniel Earwicker haklı.
David Schwartz

115

Kendimi en yüksek oyu alan cevaba katılmazken buldum, bu yüzden uzman görüşlerini aramaya gittim ve işte buradalar. Gönderen http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything

Herb Sutter: "shared_ptrs'yi geçtiğinizde, kopyalar pahalıdır"

Scott Meyers: "Değere göre mi yoksa referansla mı geçirdiğinize gelince, shared_ptr hakkında özel bir şey yoktur. Diğer herhangi bir kullanıcı tanımlı tür için kullandığınız analizi tam olarak kullanın. İnsanlar, shared_ptr'nin bir şekilde çözdüğü bu algıya sahip görünüyorlar. tüm yönetim sorunları ve küçük olduğu için, değerine göre geçiş yapmak zorunlu olarak ucuzdur. Kopyalanması gerekir ve bununla ilişkili bir maliyet vardır ... onu değerine göre aktarmak pahalıdır, bu yüzden eğer kurtulabilirsem programımda uygun anlambilimle, onun yerine const'a veya referansa atıfta bulunarak geçeceğim "

Herb Sutter: "onları her zaman const'a göre iletin ve çok nadiren belki de aradığınız şeyin referans aldığınız şeyi değiştirebileceğini bildiğiniz için, belki o zaman değere göre geçebilirsiniz ... onları parametre olarak kopyalarsanız, oh Aman tanrım, bu referans sayımını neredeyse hiç çarpmanız gerekmiyor çünkü zaten canlı tutuluyor ve referansla geçirmeniz gerekir, lütfen bunu yapın "

Güncelleme: Herb bu konuyu burada genişletti: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ , ancak hikayenin ahlakı geçmemeniz gerektiğidir "mülkiyeti paylaşmak veya devretmek gibi akıllı işaretçinin kendisini kullanmak veya değiştirmek istemiyorsanız"


8
Güzel bul! Konuyla ilgili en önde gelen iki uzmanın SO konusundaki geleneksel görüşü kamuoyunda çürüttüğünü görmek harika.
Stephan Tolksdorf

3
"Değere göre mi yoksa referansla mı geçirdiğinize gelince, shared_ptr hakkında özel bir şey yoktur" - Buna gerçekten katılmıyorum. ÖZELDİR. Şahsen ben güvenli oynamayı ve performansında hafif bir darbe almayı tercih ederim. Optimize etmem gereken belirli bir kod alanı varsa, emin olun, paylaşılan_tr aktarımının sabit ref tarafından sağlanan performans avantajlarına bakardım.
JasonZ

3
Ayrıca, e-postaların aşırı kullanımı konusunda shared_ptrmutabakat varken, referansa göre geçiş meselesi konusunda bir anlaşma olmadığını belirtmek de ilginçtir .
Nicol bolas

2
Scott Meyers: "eğer programımda uygun anlambilimden kurtulabilirsem ..." yani cevabımla hiç çelişmez, bu da parametreleri değiştirmenin const &anlambilimi etkileyip etkilemeyeceğini anlamanın çok kolay olduğuna işaret eder. basit programlar.
Daniel Earwicker

7
Herb Sutter: "çok nadiren belki de aradığınız şeyi bildiğiniz için referans aldığınız şeyi değiştirebilir". Yine, küçük bir dava için o küçük muafiyet, bu yüzden cevabımla çelişmiyor. Asıl soru kalır: const ref kullanmanın güvenli olduğunu nasıl anlarsınız ? Basit bir programda kanıtlamak çok kolay, karmaşık bir programda o kadar kolay değil. Ama hey, bu ise sağ, C ++, ve biz üzerinde hemen hemen tüm diğer mühendislik kaygıları prematüre mikro optimizasyonu lehine böylece ?! :)
Daniel Earwicker

22

Siz ve birlikte çalıştığınız diğer programcılar gerçekten ne yaptığınızı gerçekten bilmiyorsanız , bu uygulamaya karşı tavsiyede bulunacağım .

Birincisi, sınıfınızın arayüzünün nasıl gelişeceği hakkında hiçbir fikriniz yok ve diğer programcıların kötü şeyler yapmasını engellemek istiyorsunuz. Bir paylaşılan_tr'yi referans olarak geçirmek, bir programcının görmeyi beklemesi gereken bir şey değildir, çünkü bu deyimsel değildir ve bu onu yanlış bir şekilde kullanmayı kolaylaştırır. Savunmaya yönelik programlayın: Arayüzün yanlış kullanımını zorlaştırın. Referans olarak geçmek, daha sonra problemleri davet edecek.

İkinci olarak, bu belirli sınıfın bir sorun olacağını öğrenene kadar optimizasyon yapmayın. Önce profil, sonra programınız gerçekten referansla geçerek verilen desteğe ihtiyaç duyuyorsa, o zaman belki. Aksi takdirde, küçük şeylerle uğraşmayın (yani değere göre geçmek için gereken ekstra N talimat), bunun yerine tasarım, veri yapıları, algoritmalar ve uzun vadeli sürdürülebilirlik hakkında endişelenmeyin.


Litb'in cevabı teknik olarak doğru olsa da, programcıların "tembelliğini" asla küçümsemeyin (ben de tembelim!). littlenag'ın cevabı daha iyidir, paylaşılan bir_tr'ye yapılan bir referansın beklenmedik olacağı ve muhtemelen (muhtemelen) gelecekteki bakımı daha zor hale getiren gereksiz bir optimizasyon olacağıdır.
netjeff

18

Evet, orada referans almak iyidir. Yönteme paylaşılan sahiplik verme niyetinde değilsiniz; sadece onunla çalışmak istiyor. Yine de kopyaladığınız için ilk vaka için de referans alabilirsiniz. Ancak ilk durumda sahiplik alır . Hala yalnızca bir kez kopyalamak için bir numara var:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

Ayrıca iade ettiğinizde de kopyalamanız gerekir (yani bir referans döndürmemelisiniz). Çünkü sınıfınız müşterinin onunla ne yaptığını bilmiyor (ona bir işaretçi depolayabilir ve sonra büyük patlama olur). Daha sonra bunun bir darboğaz olduğu ortaya çıkarsa (ilk profil!), O zaman yine de bir referans döndürebilirsiniz.


Düzenleme : Elbette, diğerlerinin de belirttiği gibi, bu yalnızca kodunuzu biliyorsanız ve geçirilen paylaşılan işaretçiyi bir şekilde sıfırlamadığınızı biliyorsanız doğrudur. Şüpheniz varsa, sadece değere göre aktarın.


11

Geçmek mantıklı shared_ptrtarafından s const&. Muhtemelen soruna yol açmayacaktır ( shared_ptrEarwicker tarafından detaylandırıldığı üzere, fonksiyon çağrısı sırasında referans verilenin silinmesi beklenmedik durumlar hariç ) ve bunların çoğunu etrafta dolaştırırsanız muhtemelen daha hızlı olacaktır. Hatırlamak; varsayılan boost::shared_ptriş parçacığı güvenlidir, bu nedenle kopyalanması iş parçacığı için güvenli bir artış içerir.

Geçici nesneler const olmayan başvurular tarafından aktarılamayabileceğinden, const&yalnızca kullanmayı deneyin &. (MSVC'deki bir dil uzantısı yine de yapmanıza izin verse bile)


3
Evet, her zaman const referanslar kullanırım, sadece örneğime koymayı unuttum. Her neyse, MSVC, const olmayan başvuruları bir hata için değil geçici dosyalara bağlamaya izin verir, ancak varsayılan olarak "C / C ++ -> Dil -> Dil Uzantısını Devre Dışı Bırak" özelliği "HAYIR" olarak ayarlandığı için. Etkinleştirin ve onları derlemeyecek ...
abigagli

abigagli: Cidden mi? Tatlı! Bunu iş yerinde uygulayacağım, yarın ilk iş;)
Magnus Hoff

10

İkinci durumda, bunu yapmak daha kolaydır:

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

Diye çağırabilirsin

only_work_with_sp(*sp);

3
İşaretçinin bir kopyasını almanız gerekmediğinde nesne referanslarını kullanmak için kuralı benimserseniz, bu aynı zamanda amacınızı belgelemeye de hizmet eder. Ayrıca bir const referansı kullanma şansı verir.
Mark Ransom

Evet, çağrılan işlevin o nesne hakkında hiçbir şeyi "hatırlamadığını" ifade etmek için nesnelere yapılan göndermelerin kullanımına katılıyorum. İşlev nesnenin "izini
sürüyorsa

3

Fonksiyon açıkça göstericiyi değiştirmediği sürece "düz" bir referanstan kaçınırım.

A const &, küçük işlevler çağrılırken mantıklı bir mikro optimizasyon olabilir - örneğin, bazı koşulları sıraya dizmek gibi daha fazla optimizasyonu etkinleştirmek için. Ayrıca, artırma / azaltma - iş parçacığı güvenli olduğundan - bir senkronizasyon noktasıdır. Yine de bunun çoğu senaryoda büyük bir fark yaratmasını beklemiyorum.

Genel olarak, yapmamak için bir nedeniniz olmadıkça daha basit stili kullanmalısınız. Ardından, ya const &tutarlı bir şekilde kullanın ya da yalnızca birkaç yerde kullanıyorsanız neden olduğuna dair bir yorum ekleyin.


3

Ben, paylaşımlı göstericiyi const başvurusu ile geçirmeyi savunurdum - gösterici ile iletilen işlevin işaretçiye sahip olmadığı bir anlambilim, bu geliştiriciler için temiz bir deyimdir.

Tek sorun, çoklu evre programlarında, paylaşılan işaretçi tarafından işaret edilen nesnenin başka bir evrede yok edilmesidir. Bu nedenle, paylaşılan göstericinin const referansını kullanmanın tek iş parçacıklı programda güvenli olduğunu söylemek güvenlidir.

Ortak göstericinin const olmayan bir referansla iletilmesi bazen tehlikelidir - bunun nedeni, işlevin döndükten sonra hala geçerli olduğu düşünülen nesneyi yok etmek için işlevin içinde başlatabileceği takas ve sıfırlama işlevleridir.

Sanırım bu, erken optimizasyonla ilgili değil - ne yapmak istediğinizi netleştirdiğinizde ve kodlama deyimi diğer geliştiricileriniz tarafından sıkı bir şekilde benimsendiğinde gereksiz CPU döngü israfından kaçınmakla ilgilidir.

Sadece benim 2 sentim :-)


1
David Schwartz'ın yukarıdaki yorumuna bakın "... Paylaşılan bir işaretçiye bir referansı iletmekten kaynaklanan çok ciddi bir hatayı takip ettim. Kod bir nesnenin durum değişikliğini işliyordu ve nesnenin durumunun değiştiğini fark ettiğinde, onu önceki durumdaki nesnelerin koleksiyonundan çıkardı ve yeni durumdaki nesneler koleksiyonuna taşıdı. Kaldırma işlemi nesneye yönelik son paylaşılan işaretçiyi yok etti. Üye işlevi, paylaşılan işaretçiye bir başvuru üzerine çağrıldı koleksiyonda. Boom ... "
Jason Harrison

3

Görünüşe göre buradaki tüm artılar ve eksiler, yalnızca shared_ptr'ye değil, referansla iletilen HERHANGİ bir türe genelleştirilebilir. Bana göre referans, const referans ve değer ile geçişin anlamını bilmeli ve doğru kullanmalısınız. Ancak, tüm referansların kötü olduğunu düşünmediğiniz sürece, paylaşılan_ptr'yi referans olarak iletmenin doğası gereği yanlış bir şey yoktur ...

Örneğe geri dönmek için:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

sp.do_something()Sarkan bir işaretçi yüzünden patlamayacağını nereden biliyorsun ?

Gerçek şu ki, paylaşılan_tr veya değil, sabit olsun veya olmasın, bu sp, iş parçacıkları arasında sahipliğin doğrudan veya dolaylı olarak paylaşılması, bunu yapan bir nesnenin eksik delete thisolması, döngüsel bir sahiplik veya diğer sahiplik hatalarının olması gibi bir tasarım kusurunuz varsa olabilir .


2

Henüz bahsetmediğim bir şey, paylaşılan işaretçileri başvuruya göre ilettiğinizde, türetilmiş bir sınıf paylaşılan işaretçisini bir temel sınıf paylaşılan işaretçisine başvuru üzerinden geçirmek istiyorsanız, elde ettiğiniz örtük dönüşümü kaybedersiniz.

Örneğin, bu kod bir hata üretir, ancak değiştirirseniz çalışacaktır, test()böylece paylaşılan gösterici referans ile iletilmez.

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}

1

Erken optimizasyona aşina olduğunuzu ve bunu akademik amaçlarla ya da düşük performans gösteren önceden var olan bazı kodları izole ettiğiniz için sorduğunuzu varsayacağım .

Referansla geçmek tamamdır

Const referansıyla geçmek daha iyidir ve işaret edilen nesnede sabitliği zorlamadığı için genellikle kullanılabilir.

Sen değil nedeniyle bir başvuru kullanarak işaretçiyi kaybetme riski. Bu referans, yığında daha önce akıllı işaretçinin bir kopyasına sahip olduğunuzun ve yalnızca bir iş parçacığının bir çağrı yığınına sahip olduğunun kanıtıdır, böylece önceden var olan kopya ortadan kalkmaz.

Referans kullanmak, bahsettiğiniz nedenlerden dolayı genellikle daha etkilidir, ancak garanti edilmez . Bir nesnenin referansını kaldırmanın da işe yarayabileceğini unutmayın. İdeal referans kullanım senaryonuz, kodlama stilinizin birçok küçük işlevi içermesi ve işaretçinin kullanılmadan önce işlevden işleve geçmesi olabilir.

Sen gerektiğini hep saklamak önlemek referans olarak akıllı işaretçi. Sizin Class::take_copy_of_sp(&sp)örnek gösterir bunun için kullanımını düzeltin.


1
"Bir referans kullanmaktan dolayı işaretçiyi kaybetme riskiniz yok. Bu referans, yığında daha önce akıllı işaretçinin bir kopyasına sahip olduğunuzun kanıtıdır" Veya bir veri üyesi ...?
Daniel Earwicker

Boost :: thread ve boost :: ref: boost :: function <int> functionPointer = boost :: bind (doSomething, boost :: ref (sharedPtrInstance)); m_workerThread = new boost :: thread (functionPointer); ... paylaşılanPtrInstance'i sil
Jason Harrison

1

Const doğruluğu ile ilgilenmediğimizi varsayarsak (veya daha fazlası, işlevlerin, iletilen verilerin sahipliğini değiştirmesine veya paylaşmasına izin vermeyi kastediyorsunuz), değere göre bir boost :: shared_ptr geçirmek, onu referans olarak iletmekten daha güvenlidir. orijinal boost :: shared_ptr'nin kendi yaşam süresini kontrol etmesine izin veriyoruz. Aşağıdaki kodun sonuçlarını düşünün ...

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

Yukarıdaki örnekten , sharedA'yı referans olarak geçirmenin , FooTakesReference'ın orijinal işaretçiyi sıfırlamasına izin verdiğini görüyoruz , bu da kullanım sayısını 0'a düşürerek verilerini yok ediyor. Bununla birlikte, FooTakesValue orijinal işaretçiyi sıfırlayamaz, bu da sharedB'nin verilerinin hala kullanılabilir olmasını garanti eder . Başka bir geliştirici kaçınılmaz olarak ortaya çıktığında ve paylaşılan A'nın kırılgan varlığını sürdürmeye çalıştığında , kaos ortaya çıkar. Ancak şanslı paylaşılan B geliştiricisi, kendi dünyasında her şey yolunda olduğu için eve erken döner.

Bu durumda kod güvenliği, kopyalamanın yarattığı hız iyileştirmelerinden çok daha ağır basar. Aynı zamanda boost :: shared_ptr, kod güvenliğini artırmak içindir. Bir şey bu tür bir niş optimizasyonu gerektiriyorsa, bir kopyadan referansa gitmek çok daha kolay olacaktır.


1

Sandy şöyle yazdı: "Görünüşe göre buradaki tüm artılar ve eksiler, yalnızca paylaşılan_tr. Referansla iletilen HERHANGİ bir türe genelleştirilebilir."

Bir dereceye kadar doğru, ancak shared_ptr kullanmanın amacı, nesne yaşam süreleriyle ilgili endişeleri ortadan kaldırmak ve derleyicinin bunu sizin için halletmesine izin vermektir. Paylaşılan bir işaretçiyi başvuruya göre geçirecekseniz ve nesne verilerini serbest bırakabilecek referans sayılan nesne istemcilerinin const olmayan yöntemlerini çağırmasına izin verecekseniz, paylaşılan bir işaretçi kullanmak neredeyse anlamsızdır.

Önceki cümlede "neredeyse" yazdım çünkü performans bir endişe kaynağı olabilir ve nadir durumlarda haklı görülebilir, ancak bu senaryodan kendim de kaçınır ve ciddi şekilde bakmak gibi diğer tüm olası optimizasyon çözümlerini kendim arardım başka bir yönlendirme düzeyi, tembel değerlendirme vb.

Yazarının geçmişinde var olan veya hatta yazarın belleğini gönderen kod, davranış hakkında, özellikle de nesne yaşam süreleriyle ilgili davranış hakkında örtük varsayımlar gerektiren, açık, özlü, okunabilir dokümantasyon gerektirir ve bu durumda birçok istemci onu yine de okumaz! Basitlik neredeyse her zaman verimlilikten üstündür ve neredeyse her zaman verimli olmanın başka yolları vardır. Referans sayılan nesnelerinizin (ve eşittir operatörünün) kopya yapıcıları tarafından derin kopyalamadan kaçınmak için değerleri referansla geçirmeniz gerekiyorsa, belki de derin kopyalanmış verileri referans sayılan işaretçiler haline getirmenin yollarını düşünmelisiniz. hızlı kopyalandı. (Elbette, bu sizin durumunuz için geçerli olmayabilecek bir tasarım senaryosudur).


1

Akıllı işaretçileri değere göre geçirme ilkesinin çok güçlü olduğu bir projede çalışıyordum. Bazı performans analizi yapmam istendiğinde - akıllı işaretçilerin referans sayaçlarının artırılması ve azaltılması için uygulamanın kullanılan işlemci süresinin% 4-6'sını harcadığını buldum.

Daniel Earwicker'dan açıklanan garip durumlarda sorun yaşamamak için akıllı işaretçileri değere göre geçmek istiyorsanız, ödediğiniz fiyatı anladığınızdan emin olun.

Bir referansla devam etmeye karar verirseniz, const referansını kullanmanın ana nedeni, arabirimde kullandığınız sınıfı miras alan sınıftan nesneye paylaşılan işaretçiyi iletmeniz gerektiğinde örtük yukarı tahmini mümkün kılmaktır.


0

Litb'in söylediğine ek olarak, ikinci örnekte muhtemelen const referansıyla geçeceğini belirtmek isterim, bu şekilde yanlışlıkla değiştirmeyeceğinizden emin olursunuz.


0
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. değere göre geçiş:

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
  2. referansla geçmek:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
  3. işaretçi ile geçmek:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }

0

Her kod parçası bir anlam taşımalıdır. Uygulamanın her yerine değerine göre paylaşılan bir işaretçi iletirseniz , bu, "Başka bir yerde neler olup bittiğinden emin değilim, bu nedenle ham güvenliği tercih ederim" anlamına gelir . Bu, koda başvurabilecek diğer programcılar için iyi bir güven işareti olarak adlandırdığım şey değil.

Her neyse, bir işlev bir const başvurusu alsa ve "emin değilseniz", işaretçiye güçlü bir başvuru eklemek için işlevin başında paylaşılan işaretçinin bir kopyasını yine de oluşturabilirsiniz. Bu aynı zamanda tasarım hakkında bir ipucu olarak da görülebilir ("işaretçi başka bir yerde değiştirilebilir").

Yani evet, IMO, varsayılan " sabit referansla geçirme " olmalıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.