Tüm nesneleri mi yoksa işaretçileri kaplarda mı saklamalıyım?


162

Sıfırdan yeni bir sistem tasarlamak. Uzun ömürlü bazı nesnelerin listelerini ve haritalarını saklamak için STL'yi kullanacağım.

Soru: Nesnelerimin kopya oluşturucularına sahip olmasını ve nesnelerin kopyalarını STL kaplarımda saklamasını mı sağlamalıyım yoksa yaşamı ve kapsamı kendim yönetmek ve yalnızca işaretleyicileri bu nesnelere STL kaplarımda saklamak daha mı iyi?

Bunun ayrıntılarda biraz kısa olduğunun farkındayım, ancak varsa "teorik" daha iyi cevabı arıyorum, çünkü bu çözümlerin her ikisinin de mümkün olduğunu biliyorum.

İşaretçilerle oynamanın çok açık iki dezavantajı: 1) Bu nesnelerin tahsis / dağıtılmasını STL'nin ötesinde bir kapsamda yönetmeliyim. 2) Yığın üzerinde geçici bir nesne oluşturup kaplarıma ekleyemiyorum.

Kaçırdığım başka bir şey var mı?


36
tanrı bu siteyi seviyorum, bu bugün düşündüğüm kesin soru ... benim için sorma işi yaptığınız için teşekkürler :-)
eviljack

2
bir başka ilginç şey, işaretçinin koleksiyona gerçekten eklenip eklenmediğini kontrol etmemiz ve eğer değilse, bellek sızıntılarından kaçınmak için delete'i çağırmamız gerekir ... if ((set.insert (pointer)). second = false) {işaretçiyi sil;}
javapower

Yanıtlar:


68

İnsanlar işaretçi kullanmanın etkinliği konusunda kararlıdır.

Bir std :: vector kullanmayı düşünüyorsanız ve güncellemeler azsa ve koleksiyonunuz üzerinde sık sık yineliyorsanız ve daha iyi bir referans konumu alacağınızdan, polimorfik olmayan bir tür saklama nesnesi "kopyalar" daha verimli olacaktır.

Otoh, eğer güncellemeler yaygınsa, işaretçiler kopyalama / yer değiştirme maliyetlerini koruyacaktır.


7
Önbellek mevkisi açısından, işaretçileri vektöre depolamak, pointeler için özel bir ayırıcı ile birlikte kullanıldığında etkili olabilir. Özel ayırıcı, örneğin yeni yerleşimi kullanarak önbellek konumuyla ilgilenmelidir (bkz. En.wikipedia.org/wiki/Placement_syntax#Custom_allocators ).
amit

47

Bu gerçekten sizin durumunuza bağlıdır.

Nesneleriniz küçükse ve nesnenin bir kopyasını yapmak hafifse, verilerin bir stl kapsayıcısında saklanması basittir ve benim görüşüme göre yönetimi daha kolaydır, çünkü ömür boyu yönetim hakkında endişelenmenize gerek yoktur.

Nesneleriniz büyükse ve varsayılan bir kurucuya sahip olmak mantıklı değilse veya nesnelerin kopyaları pahalıysa, işaretçilerle depolamak muhtemelen yoludur.

Nesnelere işaretçi kullanmaya karar verirseniz, Boost Pointer Container Library'ye bakın . Bu destek kitaplığı, dinamik olarak ayrılmış nesnelerle kullanılmak üzere tüm STL kaplarını sarar.

Her işaretçi kabı (örneğin ptr_vector), kaba eklendiğinde bir nesnenin sahipliğini alır ve bu nesnelerin ömrünü sizin için yönetir. Ayrıca referans olarak bir ptr_ kapsayıcısındaki tüm öğelere erişebilirsiniz. Bu, aşağıdaki gibi şeyler yapmanızı sağlar

class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive( "Lexus", 57700 ) );
bigVector.push_back( new BigExpensive( "House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

Bu sınıflar STL kapsayıcılarını sarar ve gerçekten kullanışlı olan tüm STL algoritmalarıyla çalışır.

Kaptaki bir işaretçinin sahipliğini arayana aktarma olanakları da vardır (kapların çoğundaki serbest bırakma işlevi aracılığıyla).


38

Polimorfik nesneleri saklıyorsanız, her zaman bir temel sınıf işaretçisi koleksiyonu kullanmanız gerekir.

Yani koleksiyonunuzda farklı türetilmiş türleri saklamayı planlıyorsanız, işaretçileri saklamanız veya dilimleme deamonu tarafından yenmeniz gerekir.


1
Dilimleme deamon sevdi!
17'de idichekop

22

Etkinlikten 3 yıl sonra atladığım için üzgünüm, ama burada bir uyarı notu ...

Son büyük projemde, merkezi veri yapım oldukça basit nesnelerden oluşuyordu. Projeye yaklaşık bir yıl kala, gereksinimler geliştikçe, nesnenin aslında polimorfik olması gerektiğini fark ettim. Veri yapısını bir dizi temel sınıf işaretçisi olarak düzeltmek ve nesne depolama, döküm vb. Kendimi yeni kodun çalıştığına ikna etmek birkaç ayımı aldı. Bu arada, bu bana C ++ 'ın nesne modelinin ne kadar iyi tasarlanmış olduğunu zorlaştırdı.

Mevcut büyük projemde, merkezi veri yapım oldukça basit nesnelerden oluşuyor. Projeye yaklaşık bir yıl (bugün olacak), nesnenin aslında polimorfik olması gerektiğini fark ettim. Ağa geri dönün, bu iş parçacığını buldum ve Nick'in Boost pointer kapsayıcı kütüphanesine bağlantısını buldu. Bu, her şeyi düzeltmek için son kez yazmak zorunda olduğum şeydi, bu yüzden bu sefer bir deneyeceğim.

Ahlaki, benim için, yine de: eğer spesifikasyonunuz taştan% 100 dökülmezse, işaretçiler için gidin ve daha sonra kendinizi çok fazla işten kurtarabilirsiniz.


Özellikler asla taşa yerleştirilmez. İşaretçi kaplarını özel olarak kullanmanız gerektiği anlamına gelmez, ancak Boost işaretçi kapları bu seçeneği çok daha çekici hale getirir. Bir nesne kabının bir işaretçi kabına dönüştürülmesi gerektiğine karar verirseniz, tüm programınızı bir kerede elden geçirmeniz gerektiğinden şüpheliyim. Bazı tasarımlarda durum böyle olabilir. Bu durumda, kırılgan bir tasarımdır. Bu durumda, sorununuzu nesne kaplarının "zayıflığı" konusunda suçlamayın.
allyourcode

Öğeyi değer semantiği ile vektörde bırakmış ve içindeki polimorfik davranışı yapmış olabilirsiniz.
Billy ONeal

19

Neden her iki dünyanın en iyisini elde edemiyorsunuz: ( boost::shared_ptrveya gibi std::shared_ptr) bir akıllı işaretçi konteyneri yapın . Belleği yönetmek zorunda değilsiniz ve büyük kopyalama işlemleri ile uğraşmak zorunda değilsiniz.


Bu yaklaşım, Boost Pointer Konteyner Kütüphanesi'ni kullanarak Nick Haddad'ın önerdiğinden nasıl farklıdır?
Thorsten Schöning

10
@ ThorstenSchöning std :: shared_ptr, takviye bağımlılığı eklemiyor.
James Johnston

Polimorfizminizi idare etmek için paylaşılan işaretçiler kullanamazsınız, bu nedenle açıkça işaretçiler
atmadıkça

11

Genellikle nesneleri doğrudan STL konteynerinde saklamak en basit, en verimli ve nesneyi kullanmak için en kolay yoldur.

Nesnenizin kendisinde kopyalanamayan bir sözdizimi varsa veya soyut bir temel türü varsa, işaretçileri depolamanız gerekir (en kolayı paylaşılan_ptr kullanmaktır)


4
Nesneleriniz büyükse ve öğeleri sık sık hareket ettiriyorsanız en verimli değildir.
allyourcode

3

Farkı iyi kavramış görünüyorsunuz. Nesneler küçükse ve kopyalanması kolaysa, elbette onları saklayın.

Değilse, öbek üzerinde tahsis olanlar için akıllı işaretçiler (auto_ptr, bir ref sayma akıllı işaretçi değil) depolamayı düşünürdüm. Açıkçası, akıllı işaretçileri tercih ederseniz, geçici yığın tahsisli nesneleri (söylediğiniz gibi) depolayamazsınız.

@ Torbjörn dilimleme konusunda iyi bir noktaya değiniyor.


1
Oh, ve asla hiç hiç auto_ptr en bir koleksiyon oluşturmak
Torbjörn Gyllebring

Doğru, auto_ptr akıllı bir işaretçi değildir - sayılmaz.
Lou Franco

auto_ptr öğesinde de tahribatsız kopya semantiği yoktur. Bir auto_ptr atama hareket oneTo anotherbaşvuruyu yayınlayacak oneve değişim one.
Andy Finkenstadt


2

Nesneler kodun başka bir yerine yönlendirilecekse, boost :: shared_ptr vektöründe saklayın. Bu, vektörü yeniden boyutlandırırsanız nesneye yönelik işaretçilerin geçerli kalmasını sağlar.

yani:

std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized

Başka hiç kimse nesnelere işaretçiler depolamazsa veya liste büyüyüp küçülmezse, yalnızca eski nesneler olarak depolayın:

std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol

1

Bu soru bir süredir beni rahatsız ediyor.

İşaretçileri saklamak için eğildim, ancak sizin için geçerli olmayan bazı ek gereksinimlerim (SWIG lua sarmalayıcıları) var.

Bu gönderideki en önemli nokta , nesnelerinizi kullanarak kendiniz test etmektir

Bugün, 10 milyon nesne, 500 kez bir üye işlevini çağırma hızını test etmek için yaptım.

İşlev, xdir ve ydir (tüm kayan öğe değişkenleri) temelinde x ve y'yi günceller.

Her iki nesne türünü tutmak için bir std :: list kullandım ve nesneyi listede saklamanın bir işaretçi kullanmaktan biraz daha hızlı olduğunu gördüm. Öte yandan, performans çok yakındı, bu yüzden uygulamanızda nasıl kullanılacağıyla ilgili.

Referans olarak, donanımımdaki -O3 ile işaretçilerin tamamlanması 41 saniye ve ham nesnelerin tamamlanması 30 saniye sürdü.

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.