it-swarm.com.de

Beispiel für die Verwendung von shared_ptr?

Hallo, ich habe heute eine Frage zu Wie man verschiedene Arten von Objekten in dasselbe Vektorarray einfügt und mein Code in dieser Frage war 

 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
 } 

und ich wollte Vektoren verwenden, also schrieb jemand, dass ich das tun sollte:

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();
}

aber dann haben er und viele andere vorgeschlagen, dass ich Boost-Zeigercontainer besser verwenden sollte
oder shared_ptr. Ich habe die letzten 3 Stunden damit verbracht, über dieses Thema zu lesen, aber die Dokumentation scheint mir ziemlich fortgeschritten zu sein. **** Kann mir jemand ein kleines Codebeispiel für die Verwendung von shared_ptr geben und warum sie die Verwendung von shared_ptr vorgeschlagen hat Es gibt auch andere Typen wie ptr_vector, ptr_list und ptr_deque ** **

Edit1: Ich habe auch ein Codebeispiel gelesen, das Folgendes enthielt:

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 );
...........
}

Und ich verstehe die Syntax nicht!

79
Ahmed

Durch die Verwendung einer vector von shared_ptr wird die Möglichkeit eines Speicherverlusts beseitigt, da Sie vergessen haben, den Vektor zu bewegen und delete für jedes Element aufzurufen. Gehen wir eine etwas modifizierte Version des Beispiels Zeile für Zeile durch.

typedef boost::shared_ptr<gate> gate_ptr;

Erstellen Sie einen Alias ​​für den gemeinsam genutzten Zeigertyp. Dadurch wird die Hässlichkeit in der C++ - Sprache vermieden, die sich aus der Eingabe von std::vector<boost::shared_ptr<gate> > und dem Vergessen des Leerzeichens zwischen den schließenden Größer-als-Zeichen ergibt.

    std::vector<gate_ptr> vec;

Erzeugt einen leeren Vektor von boost::shared_ptr<gate>-Objekten.

    gate_ptr ptr(new ANDgate);

Weisen Sie eine neue ANDgate-Instanz zu und speichern Sie sie in einem shared_ptr. Der Grund dafür, dies separat zu tun, besteht darin, ein Problem zu vermeiden, das auftreten kann, wenn eine Operation ausgelöst wird. Dies ist in diesem Beispiel nicht möglich. Der Boost shared_ptr "Best Practices" erklärt, warum es eine Best Practice-Zuordnung in ein freistehendes Objekt anstelle eines temporären Objekts ist.

    vec.Push_back(ptr);

Dies erstellt einen neuen gemeinsamen Zeiger im Vektor und kopiert ptr in ihn. Die Referenzzählung im Innern von shared_ptr gewährleistet, dass das zugewiesene Objekt in ptr sicher in den Vektor übertragen wird.

Was nicht erklärt wird, ist, dass der Destruktor für shared_ptr<gate> sicherstellt, dass der zugewiesene Speicher gelöscht wird. Hier wird der Speicherverlust vermieden. Der Destruktor für std::vector<T> stellt sicher, dass der Destruktor für T für jedes im Vektor gespeicherte Element aufgerufen wird. Der Destruktor für einen Zeiger (z. B. gate*) löscht jedoch nicht den von Ihnen zugewiesenen Speicher. Dies versuchen Sie zu vermeiden, indem Sie shared_ptr oder ptr_vector verwenden.

112
D.Shawley

Ich füge hinzu, dass eines der wichtigsten Dinge bei shared_ptr's ist, sie nur mit ever mit der folgenden Syntax zu konstruieren:

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

Auf diese Weise ist der "echte" Zeiger auf Type für Ihren Gültigkeitsbereich anonym und wird vom gemeinsamen Zeiger nur gehalten. Daher ist es unmöglich, diesen "echten" Zeiger versehentlich zu verwenden. Mit anderen Worten, mach das nie:

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

Obwohl dies funktioniert, haben Sie jetzt einen Type*-Zeiger (t_ptr) in Ihrer Funktion, der sich außerhalb des gemeinsam genutzten Zeigers befindet. Es ist gefährlich, t_ptr überall zu verwenden, da Sie nie wissen, wann der gemeinsam genutzte Zeiger, der ihn enthält, ihn zerstören kann, und Sie werden segfault.

Dasselbe gilt für Zeiger, die von anderen Klassen zurückgegeben werden. Wenn eine Klasse, die Sie nicht geschrieben haben, Ihnen einen Zeiger gibt, ist es normalerweise nicht sicher, sie einfach in einen shared_ptr zu schreiben. Es sei denn, Sie sind sicher, dass die Klasse dieses Objekt nicht mehr verwendet. Wenn Sie es jedoch in einen shared_ptr setzen und es außerhalb des Gültigkeitsbereichs liegt, wird das Objekt freigegeben, wenn die Klasse es möglicherweise noch benötigt.

40
Ken Simon

Der Umgang mit intelligenten Zeigern ist meiner Meinung nach einer der wichtigsten Schritte, um ein kompetenter C++ - Programmierer zu werden. Wie Sie wissen, wann immer Sie ein Objekt neu erstellen, möchten Sie es löschen. 

Ein Problem, das sich ergibt, ist, dass es mit Ausnahmen sehr schwer sein kann, sicherzustellen, dass ein Objekt in allen möglichen Ausführungspfaden immer nur einmal freigegeben wird.

Dies ist der Grund für RAII: http://en.wikipedia.org/wiki/RAII

Erstellen einer Hilfsklasse, um sicherzustellen, dass ein Objekt in allen Ausführungspfaden einmal gelöscht wird. 

Ein Beispiel für eine Klasse wie diese ist: std :: auto_ptr

Aber manchmal möchten Sie Objekte mit anderen teilen. Es sollte nur gelöscht werden, wenn es nicht mehr verwendet wird.

Um diese Referenz zu unterstützen, wurden Zählstrategien entwickelt, Sie müssen sich jedoch noch an addref und release ref erinnern. Im Wesentlichen ist dies das gleiche Problem wie Neu/Löschen.

Deshalb hat boost den Befehl boost :: shared_ptr entwickelt, einen intelligenten Zeiger für die Referenzzählung, mit dem Sie Objekte gemeinsam nutzen und nicht ungewollt Speicher verlieren können.

Mit dem Zusatz von C++ tr1 wird dies nun auch zum C++ - Standard hinzugefügt, jedoch mit dem Namen std :: tr1 :: shared_ptr <>.

Ich empfehle, wenn möglich, den gemeinsamen Standardzeiger zu verwenden. ptr_list, ptr_dequeue und so sind IIRC-Container für Zeigertypen. Ich ignoriere sie erst einmal.

So können wir von Ihrem Beispiel ausgehen:

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(); 
} 

Das Problem hier ist jetzt, dass immer dann, wenn G aus dem Geltungsbereich geht, die zwei zu G hinzugefügten Objekte durchgespielt werden. Lassen Sie es neu schreiben, um std :: tr1 :: shared_ptr zu verwenden

// 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(); 
} 

Wenn G den Bereich verlässt, wird der Speicher automatisch wiederhergestellt.

Als eine Übung, mit der ich Neulinge in meinem Team plagte, ist es, sie zu bitten, ihre eigene Smart-Pointer-Klasse zu schreiben. Danach verwerfen Sie die Klasse sofort und verwenden Sie sie nie wieder. Hoffentlich haben Sie entscheidendes Wissen darüber erworben, wie ein intelligenter Zeiger unter der Haube funktioniert. Es gibt wirklich keine Magie.

Die Boost-Dokumentation bietet ein ziemlich gutes Startbeispiel: shared_ptr Beispiel (es handelt sich tatsächlich um einen Vektor von intelligenten Zeigern) oder shared_ptr doc Die folgende Antwort von Johannes Schaub erläutert das Smart-Pointer ziemlich gut ankurbeln: Smart-Pointer erklärt

Die Idee hinter (in so wenigen Worten wie möglich) ptr_vector ist, dass es die Freigabe von Speicher hinter den gespeicherten Zeigern für Sie übernimmt: Nehmen wir an, Sie haben einen Zeigervektor wie in Ihrem Beispiel. Wenn Sie die Anwendung beenden oder den Gültigkeitsbereich verlassen, in dem der Vektor definiert ist, müssen Sie den Vorgang bereinigen (Sie haben ANDgate und ORgate dynamisch zugewiesen). Wenn Sie jedoch nur den Vektor löschen, wird dies nicht ausgeführt, da der Vektor die Zeiger speichert und nicht die tatsächlichen Objekte (es zerstört nicht, aber was es enthält).

 // 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 <> wird das Obige für Sie erledigen - das bedeutet, dass der Speicher hinter den gespeicherten Zeigern freigegeben wird.

2
celavek

Mit Boost können Sie das tun >

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);

> zum Einfügen eines anderen Objekttyps in Ihren Vektorcontainer. Für den Zugriff müssen Sie any_cast verwenden, das wie dynamic_cast funktioniert, und hofft, dass es für Ihre Anforderungen geeignet ist. 

2
user1808932
#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;
}

Dies ist ein Beispiel für "shared_ptr" in Aktion. _obj2 wurde gelöscht, der Zeiger ist jedoch noch gültig . Die Ausgabe ist ./test. _obj1: 10 _obj2: 10 _obj2: 10 erledigt 

1
Syed Raihan

Die beste Möglichkeit, verschiedene Objekte in einen Container einzufügen, ist die Verwendung einer make_shared-, vektor- und bereichsbasierten Schleife. Sie erhalten einen schönen, sauberen und "lesbaren" Code!

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();
0
Hooman