Shared_ptr kullanma örneği?


82

Merhaba bugün aynı vektör dizisine farklı türden nesneler nasıl eklenir hakkında bir soru sordum ve bu sorudaki kodum

 gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
 .....
 ......
 virtual void Run()
   {   //A virtual function
   }
};
class ANDgate :public gate 
  {.....
   .......
   void Run()
   {
    //AND version of Run
   }  

};
 class ORgate :public gate 
  {.....
   .......
   void Run()
   {
    //OR version of Run
   }  

};      
//Running the simulator using overloading concept
 for(...;...;..)
 {
  G[i]->Run() ;  //will run perfectly the right Run for the right Gate type
 } 

ve vektörleri kullanmak istedim, böylece birisi bunu yapmam gerektiğini yazdı:

std::vector<gate*> G;
G.push_back(new ANDgate); 
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
  G[i]->Run();
}

ama sonra o ve diğerleri Boost işaretçi kaplarını
veya shared_ptr. Son 3 saati bu konuyu okuyarak geçirdim, ancak belgeler bana oldukça gelişmiş görünüyor. **** Herkes bana küçük bir shared_ptrkullanım kodu örneği verebilir ve neden kullanmayı önerdiler shared_ptr? Ayrıca ptr_vector, ptr_listve ptr_deque** ** gibi başka türler de var

Düzenleme1: Şunları da içeren bir kod örneği okudum:

typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
  std::vector<FooPtr>         foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
  foo_vector.push_back( foo_ptr );
...........
}

Ve sözdizimini anlamıyorum!


2
Hangi sözdizimini anlamıyorsun? Öğesinin ilk satırı, mainadı verilen bir türe ilişkin paylaşılan işaretçiler içerebilen bir vektör oluşturur Foo; İkincisi ise, bir oluşturur Fookullanarak newve ortak işaretçi onu yönetmek; üçüncüsü, paylaşılan göstericinin bir kopyasını vektöre koyar.
Mike Seymour

Yanıtlar:


116

Bir Kullanarak vectorait shared_ptruzaklaşmaların Eğer vektör ve çağrıyı yürümeye unuttuğu için bellek sızıntısı ihtimalini deleteher öğe üzerinde. Örneğin biraz değiştirilmiş bir versiyonunu satır satır inceleyelim.

typedef boost::shared_ptr<gate> gate_ptr;

Paylaşılan işaretçi türü için bir takma ad oluşturun. Bu, C ++ dilindeki std::vector<boost::shared_ptr<gate> >kapanış büyüktür işaretleri arasındaki boşluğun yazılması ve unutulmasından kaynaklanan çirkinliği önler .

    std::vector<gate_ptr> vec;

Boş bir boost::shared_ptr<gate>nesne vektörü oluşturur .

    gate_ptr ptr(new ANDgate);

Yeni bir ANDgateörnek tahsis edin ve bunu bir shared_ptr. Bunu ayrı ayrı yapmanın nedeni, bir işlem attığında oluşabilecek bir sorunu önlemektir. Bu örnekte bu mümkün değildir. Boost shared_ptr"En İyi Uygulamalar" bir neden açıklamak iyi uygulama yerine geçici bir ayaklı nesnesine tahsis etmek.

    vec.push_back(ptr);

Bu, vektörde yeni bir paylaşılan işaretçi oluşturur ve ptriçine kopyalar . Bağırsaklarındaki referans sayımı, shared_ptriçindeki tahsis edilen nesnenin ptrvektöre güvenli bir şekilde aktarılmasını sağlar.

shared_ptr<gate>Açıklanmayan şey, yıkıcının tahsis edilen belleğin silinmesini sağlamasıdır . Bellek sızıntısının önlendiği yer burasıdır. Yıkıcı std::vector<T>aktarımı sağlar yıkıcı bu Tvektör içinde saklanan her eleman için çağrılır. Ancak, bir göstericinin yıkıcısı (örn. gate*) , Tahsis ettiğiniz belleği silmez . shared_ptrVeya kullanarak kaçınmaya çalıştığınız şey budur ptr_vector.


1
Bu ayrıntılıydı :). Sorum, gate_ptr ptr (yeni ANDgate) kodunun 3. satırıyla ilgili; Bana pek tanıdık gelmiyor, ptr bir tür paylaşımlı işaretçi kapısı ve sonra parantezler arasında yeni bir ANDgate gönderdiniz! Bu kafa karıştırıcı.
Ahmed

6
@Ahmed: genel ifade, tıpkı 5 değeriyle int x(5);ilklendirmek xiçin yazabileceğiniz gibi, değişken bir ilklendirmedir . Bu durumda, bir ANDgate; yeni ifadenin değeri, yeni nesneye bir göstericidir.
Mike Seymour

42

Ben ilgili önemli şeylerden Şunu katacak shared_ptrler sadece etmektir' Hiç aşağıdaki sözdizimi ile inşa:

shared_ptr<Type>(new Type(...));

Bu şekilde, "gerçek" işaretçisi Typekapsamınız için anonimdir ve yalnızca paylaşılan işaretçi tarafından tutulur . Bu nedenle, yanlışlıkla bu "gerçek" işaretçiyi kullanmanız imkansız olacaktır. Başka bir deyişle, bunu asla yapmayın:

Type* t_ptr = new Type(...);
shared_ptr<Type> t_sptr ptrT(t_ptr);
//t_ptr is still hanging around!  Don't use it!

Bu işe yarayacak olsa da, artık işlevinizde paylaşılan işaretçinin dışında yaşayan bir Type*işaretçi ( t_ptr) var . t_ptrHerhangi bir yerde kullanmak tehlikelidir , çünkü onu tutan paylaşılan işaretçinin onu ne zaman yok edebileceğini asla bilemezsiniz ve ayrılacaksınız.

Aynı şey diğer sınıflar tarafından size iade edilen işaretçiler için de geçerli. Eğer bir sınıf size bir işaretçi yazmadıysa, genellikle bir shared_ptr. Sınıfın artık o nesneyi kullanmadığından emin değilseniz , hayır. Çünkü eğer onu a'ya koyarsanız shared_ptrve kapsam dışına çıkarsa, sınıf hala ihtiyaç duyduğunda nesne serbest kalır.


8
İyi ve doğru, ama ben şimdi onu aramak için tercih inanabiliyor herşey Ken söyledi auto t_ptr = make_shared<Type>(...);ya da eşit şekilde shared_ptr<Type> t_ptr = make_shared<Type>(...);form daha verimlidir çünkü.
Jason Sydes

@KenSimon, ,arasında t_sptrve ptrTiçinde virgül olması gerekiyor shared_ptr<Type> t_sptr ptrT(t_ptr);mu?
Allanqunzi

Örnek koddaki belirsizliklerin yanı sıra, iyi bir uyarı - ama utanç verici, çünkü 1. biçim çok daha temiz ve belki de daha da önemlisi, akıllı bir işaretçi kullanan herkes, kesinlikle tehlikeli ham maddeden kaçınmak için var olduğunu biliyor. etrafında yüzen işaretçiler. Son paragraf ilginç; Neyse ki, beni ham veya belirsiz türdeki noktaları kullanmaya zorlayan herhangi bir kütüphaneyle henüz çalışmadım, ancak bir süre olacağından eminim.
underscore_d

20

Akıllı işaretçileri kullanmayı öğrenmek, bence yetkin bir C ++ programcısı olmanın en önemli adımlarından biridir. Bildiğiniz gibi, ne zaman yeni bir nesne oluştursanız, bir noktada onu silmek istersiniz.

Ortaya çıkan bir sorun, istisnalar dışında, bir nesnenin tüm olası yürütme yollarında her zaman yalnızca bir kez serbest bırakıldığından emin olmanın çok zor olabileceğidir.

RAII'nin nedeni budur: http://en.wikipedia.org/wiki/RAII

Bir nesnenin tüm yürütme yollarında her zaman bir kez silindiğinden emin olmak amacıyla yardımcı bir sınıf oluşturma.

Bunun gibi bir sınıf örneği: std :: auto_ptr

Ama bazen nesneleri başkalarıyla paylaşmayı seviyorsunuz. Yalnızca artık kimse kullanmadığında silinmelidir.

Bu referans sayma stratejilerine yardımcı olmak için geliştirildi, ancak yine de addref'i hatırlamanız ve ref'i manuel olarak yayınlamanız gerekir. Özünde bu, yeni / sil ile aynı sorundur.

Bu nedenle boost, boost :: shared_ptr geliştirdi, bu, nesneleri paylaşabilmeniz ve istemeden bellek sızdırmaması için akıllı işaretçiyi sayan bir referans.

C ++ tr1'in eklenmesiyle bu artık c ++ standardına da eklenmiştir, ancak adı std :: tr1 :: shared_ptr <>.

Mümkünse standart paylaşılan işaretçiyi kullanmanızı öneririm. ptr_list, ptr_dequeue ve benzeri IIRC işaretçi türleri için özel kaplar. Onları şimdilik görmezden geliyorum.

Böylece sizin örneğinizden başlayabiliriz:

std::vector<gate*> G; 
G.push_back(new ANDgate);  
G.push_back(new ORgate); 
for(unsigned i=0;i<G.size();++i) 
{ 
  G[i]->Run(); 
} 

Buradaki problem şu ki, G kapsam dışına çıktığında G'ye eklenen 2 nesneyi sızdırıyoruz. Std :: tr1 :: shared_ptr kullanmak için yeniden yazalım.

// Remember to include <memory> for shared_ptr
// First do an alias for std::tr1::shared_ptr<gate> so we don't have to 
// type that in every place. Call it gate_ptr. This is what typedef does.
typedef std::tr1::shared_ptr<gate> gate_ptr;    
// gate_ptr is now our "smart" pointer. So let's make a vector out of it.
std::vector<gate_ptr> G; 
// these smart_ptrs can't be implicitly created from gate* we have to be explicit about it
// gate_ptr (new ANDgate), it's a good thing:
G.push_back(gate_ptr (new ANDgate));  
G.push_back(gate_ptr (new ORgate)); 
for(unsigned i=0;i<G.size();++i) 
{ 
   G[i]->Run(); 
} 

G kapsam dışına çıktığında, bellek otomatik olarak geri alınır.

Ekibimdeki yeni gelenleri rahatsız ettiğim bir egzersiz olarak, kendi akıllı işaretçi sınıflarını yazmalarını istemek. İşiniz bittikten sonra sınıfı hemen atın ve bir daha asla kullanmayın. Umarım akıllı bir işaretçinin kaputun altında nasıl çalıştığı konusunda önemli bilgiler edindiniz. Gerçekten sihir yok.


Eğitmenim de kendi derslerimi yazma konusunda bana benzer bir tavsiye verdi, bu yüzden kesinlikle deneyeceğim. TY.
Ahmed

tüm kapıları çalıştırmak için bir yineleyici kullanmalısınızfor( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
Guillaume Massé

1
Ya da daha iyisi, C ++ 'daki yeni "foreach"
Sadece başka bir metaprogramcı

2

Arttırma belgeleri oldukça iyi bir başlangıç ​​örneği sağlar: shared_ptr örneği (aslında akıllı işaretçilerden oluşan bir vektörle ilgilidir) veya shared_ptr belgesi Johannes Schaub'ın aşağıdaki cevabı, akıllı işaretçileri oldukça iyi açıklıyor: akıllı işaretçiler açıkladı

(Mümkün olduğunca az kelimeyle) ptr_vector'ın arkasındaki fikir, sizin için depolanan işaretçilerin arkasındaki belleğin serbest bırakılmasını ele almasıdır: Diyelim ki, örneğinizdeki gibi bir işaretçi vektörünüz var. Uygulamadan çıkarken veya vektörün tanımlandığı kapsamı terk ederken, kendinizden sonra temizlemeniz gerekir (dinamik olarak ANDgate ve ORgate tahsis ettiniz), ancak vektörü temizlemek bunu yapmaz çünkü vektör işaretçileri saklıyor ve gerçek nesneler değil (yok etmeyecek ama içerdiklerini).

 // if you just do
 G.clear() // will clear the vector but you'll be left with 2 memory leaks
 ...
// to properly clean the vector and the objects behind it
for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++)
{
  delete (*it);
}

boost :: ptr_vector <> yukarıdakileri sizin için halledecektir - yani depoladığı işaretçilerin arkasındaki hafızayı serbest bırakacaktır.


shared_ptr akıllı bir işaretleyicidir - basit bir işaretçi için parlak bir "sarmalayıcı" olup, diyelim ki bir işaretçi tipine biraz yapay zeka ekler. ptr_vector, işaretçiler için akıllı bir kaptır - işaretçilerden oluşan bir kap için bir "sarmalayıcı"
celavek

ptr_vector, normal vektörün yerine geçer mi?
Ahmed

@Ahmed Sanırım böyle düşünebilirsiniz.
celavek

2

Boost aracılığıyla bunu yapabilirsiniz>

std::vector<boost::any> vecobj;
    boost::shared_ptr<string> sharedString1(new string("abcdxyz!"));    
    boost::shared_ptr<int> sharedint1(new int(10));
    vecobj.push_back(sharedString1);
    vecobj.push_back(sharedint1);

> vektör kabınıza farklı nesne türü eklemek için. erişim için, dynamic_cast gibi çalışan any_cast'i kullanmanız gerekse de, ihtiyacınızı karşılayacağını umuyor.


1
#include <memory>
#include <iostream>

class SharedMemory {
    public: 
        SharedMemory(int* x):_capture(x){}
        int* get() { return (_capture.get()); }
    protected:
        std::shared_ptr<int> _capture;
};

int main(int , char**){
    SharedMemory *_obj1= new SharedMemory(new int(10));
    SharedMemory *_obj2 = new SharedMemory(*_obj1);
    std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get()
    << std::endl;
    delete _obj2;

    std::cout << " _obj1: " << *_obj1->get() << std::endl;
    delete _obj1;
    std::cout << " done " << std::endl;
}

Bu, bir shared_ptr örneğidir. _obj2 silindi, ancak işaretçi hala geçerli. çıktı, ./test _obj1: 10 _obj2: 10 _obj2: 10 tamamlandı


0

Aynı konteynere farklı nesneler eklemenin en iyi yolu make_shared, vektör ve aralık tabanlı döngü kullanmaktır ve güzel, temiz ve "okunabilir" bir koda sahip olursunuz!

typedef std::shared_ptr<gate> Ptr   
vector<Ptr> myConatiner; 
auto andGate = std::make_shared<ANDgate>();
myConatiner.push_back(andGate );
auto orGate= std::make_shared<ORgate>();
myConatiner.push_back(orGate);

for (auto& element : myConatiner)
    element->run();
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.