Cześć Zadałem dziś pytanie o to, jak wstawiać różne typy obiektów w tej samej tablicy wektorowej, a mój kod w tym pytaniu był
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
}
a chciałem użyć wektorów więc ktoś napisał, że powinienem to zrobić:
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();
}
ale potem on i wielu innych zasugerował, że lepiej byłoby użyć kontenerów wskaźnika doładowania
lub shared_ptr
. Spędziłem ostatnie 3 godziny na czytaniu na ten temat, ale dokumentacja wydaje mi się dość zaawansowana. **** Czy ktoś może mi podać mały przykład shared_ptr
użycia kodu i dlaczego zasugerował użycie shared_ptr
. Są tam również inne typy, takie jak ptr_vector
, ptr_list
i ptr_deque
** **
Edit1: Przeczytałem też przykład kodu, który obejmował:
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 );
...........
}
I nie rozumiem składni!
c++
boost
vector
shared-ptr
smart-pointers
Ahmed
źródło
źródło
main
tworzy wektor, który może zawierać wspólne wskaźniki do typu o nazwieFoo
; druga tworzyFoo
użycienew
i wspólny wskaźnik do zarządzania nim; trzeci umieszcza kopię współdzielonego wskaźnika do wektora.Odpowiedzi:
Użycie a
vector
zshared_ptr
eliminuje możliwość wycieku pamięci, ponieważ zapomniałeś przejść przez wektor i wywołaćdelete
każdy element. Przyjrzyjmy się nieco zmodyfikowanej wersji przykładu wiersz po wierszu.typedef boost::shared_ptr<gate> gate_ptr;
Utwórz alias dla współdzielonego typu wskaźnika. Pozwala to uniknąć brzydoty języka C ++, która wynika z pisania na klawiaturze
std::vector<boost::shared_ptr<gate> >
i zapominania o spacji między zamykającymi znakami większości .std::vector<gate_ptr> vec;
Tworzy pusty wektor
boost::shared_ptr<gate>
obiektów.gate_ptr ptr(new ANDgate);
Przydziel nowe
ANDgate
wystąpienie i zapisz je w plikushared_ptr
. Powodem zrobienia tego osobno jest zapobieżenie problemowi, który może wystąpić, jeśli operacja się zgłosi. W tym przykładzie nie jest to możliwe. W sekcjishared_ptr
„ Sprawdzone metody ” Boost wyjaśniono, dlaczego najlepiej jest przydzielić do obiektu wolnostojącego zamiast tymczasowego.vec.push_back(ptr);
Tworzy to nowy wspólny wskaźnik w wektorze i kopiuje
ptr
do niego. Zliczanie referencji w jelitachshared_ptr
zapewnia, że zaalokowany obiekt wewnątrzptr
zostanie bezpiecznie przeniesiony do wektora.Nie wyjaśniono, że destruktor
shared_ptr<gate>
zapewnia usunięcie przydzielonej pamięci. W ten sposób unika się wycieku pamięci. Destruktor forstd::vector<T>
zapewnia, że destruktor forT
jest wywoływany dla każdego elementu przechowywanego w wektorze. Jednak destruktor dla wskaźnika (np.gate*
) Nie usuwa przydzielonej pamięci . Tego właśnie próbujesz uniknąć, używającshared_ptr
lubptr_vector
.źródło
int x(5);
aby zainicjalizowaćx
wartość 5. W tym przypadku jest ono inicjowane wartością nowego wyrażenia, które tworzyANDgate
; wartością nowego wyrażenia jest wskaźnik do nowego obiektu.Dodam, że jedną z najważniejszych rzeczy
shared_ptr
jest to tylko kiedykolwiek zbudować je z następującą składnią:shared_ptr<Type>(new Type(...));
W ten sposób „prawdziwy” wskaźnik do
Type
jest anonimowy dla twojego zakresu i jest utrzymywany tylko przez wskaźnik współdzielony. Dlatego nie będzie możliwe przypadkowe użycie tego „prawdziwego” wskaźnika. Innymi słowy, nigdy tego nie rób:Type* t_ptr = new Type(...); shared_ptr<Type> t_sptr ptrT(t_ptr); //t_ptr is still hanging around! Don't use it!
Chociaż to zadziała, teraz masz w funkcji
Type*
wskaźnik (t_ptr
), który znajduje się poza współdzielonym wskaźnikiem. Używanie go wt_ptr
dowolnym miejscu jest niebezpieczne , ponieważ nigdy nie wiadomo, kiedy współdzielony wskaźnik, który go trzyma, może go zniszczyć, a ty posypiesz się.To samo dotyczy wskazówek zwróconych przez inne klasy. Jeśli klasa, której nie napisałeś, podaje Ci wskaźnik, generalnie nie jest bezpiecznie umieścić go w pliku
shared_ptr
. Nie, chyba że masz pewność, że klasa nie używa już tego obiektu. Ponieważ jeśli umieścisz go w ashared_ptr
i wypadnie on poza zakres, obiekt zostanie zwolniony, gdy klasa może go nadal potrzebować.źródło
auto t_ptr = make_shared<Type>(...);
lub równoważnieshared_ptr<Type> t_ptr = make_shared<Type>(...);
, po prostu dlatego, że ta forma jest bardziej wydajna.,
międzyt_sptr
iptrT
w powinien być przecinekshared_ptr<Type> t_sptr ptrT(t_ptr);
?Nauka korzystania z inteligentnych wskaźników jest moim zdaniem jednym z najważniejszych kroków, aby zostać kompetentnym programistą C ++. Jak wiesz, za każdym razem, gdy nowy obiekt w pewnym momencie chcesz go usunąć.
Jednym z pojawiających się problemów jest to, że z wyjątkami może być bardzo trudno upewnić się, że obiekt jest wydawany zawsze tylko raz we wszystkich możliwych ścieżkach wykonywania.
To jest powód RAII: http://en.wikipedia.org/wiki/RAII
Stworzenie klasy pomocniczej w celu upewnienia się, że obiekt zawsze jest usuwany raz ze wszystkich ścieżek wykonywania.
Przykład takiej klasy to: std :: auto_ptr
Ale czasami lubisz dzielić się przedmiotami z innymi. Powinien zostać usunięty tylko wtedy, gdy nikt go już nie używa.
Aby pomóc w tych strategiach liczenia referencji, zostały opracowane, ale nadal musisz pamiętać adres i ręcznie zwolnić ref. W istocie jest to ten sam problem, co nowy / usuń.
Dlatego boost opracował metodę boost :: shared_ptr, jest to inteligentny wskaźnik zliczający odwołania, dzięki czemu można udostępniać obiekty i zapobiegać przypadkowemu wyciekowi pamięci.
Wraz z dodaniem C ++ tr1 jest to teraz również dodawane do standardu C ++, ale ma nazwę std :: tr1 :: shared_ptr <>.
Jeśli to możliwe, zalecam używanie standardowego wskaźnika współdzielonego. ptr_list, ptr_dequeue i tak samo są wyspecjalizowanymi kontenerami IIRC dla typów wskaźników. Na razie je ignoruję.
Więc możemy zacząć od twojego przykładu:
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(); }
Problem polega na tym, że za każdym razem, gdy G wychodzi poza zasięg, przeciekamy 2 obiekty dodane do G. Przepiszmy to tak, aby używały std :: tr1 :: shared_ptr
// 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(); }
Kiedy G wychodzi poza zakres, pamięć jest automatycznie przywracana.
Jako ćwiczenie, którym nękałem nowicjuszy w moim zespole, jest poproszenie ich o napisanie własnej klasy inteligentnego wskaźnika. Następnie, gdy skończysz, natychmiast odrzuć klasę i nigdy więcej jej nie używaj. Miejmy nadzieję, że zdobyłeś kluczową wiedzę na temat działania inteligentnego wskaźnika pod maską. Naprawdę nie ma magii.
źródło
for( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
Dokumentacja doładowania dostarcza całkiem niezłego przykładu początkowego: przykład shared_ptr (w rzeczywistości chodzi o wektor inteligentnych wskaźników) lub dokument shared_ptr Poniższa odpowiedź Johannesa Schauba dość dobrze wyjaśnia inteligentne wskaźniki doładowania: wyjaśniono inteligentne wskaźniki
Idea kryjąca się za (w jak najmniejszej liczbie słów) ptr_vector polega na tym, że obsługuje on za Ciebie zwolnienie pamięci za przechowywanymi wskaźnikami: powiedzmy, że masz wektor wskaźników, jak w przykładzie. Zamykając aplikację lub opuszczając zakres, w którym wektor jest zdefiniowany, będziesz musiał posprzątać po sobie (dynamicznie zaalokowałeś ANDgate i ORgate), ale samo wyczyszczenie wektora nie zrobi tego, ponieważ wektor przechowuje wskaźniki a nie rzeczywiste obiekty (nie zniszczy, ale to, co zawiera).
// 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 <> zajmie się powyższym za Ciebie - co oznacza, że zwolni pamięć za przechowywanymi wskaźnikami.
źródło
Dzięki Boost możesz to zrobić>
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);
> do wstawiania obiektów innego typu w kontenerze wektorowym. podczas gdy aby uzyskać dostęp, musisz użyć any_cast, który działa jak dynamic_cast, ma nadzieję, że zadziała dla twoich potrzeb.
źródło
#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; }
To jest przykład działania shared_ptr. _obj2 został usunięty, ale wskaźnik jest nadal prawidłowy. wyjście to ./test _obj1: 10 _obj2: 10 _obj2: 10 gotowe
źródło
Najlepszym sposobem na dodanie różnych obiektów do tego samego kontenera jest użycie pętli make_shared, wektor i zakres, a otrzymasz ładny, czysty i „czytelny” kod!
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();
źródło