Jeśli zadeklaruję obiekt zawinięty we współdzielony wskaźnik:
std::shared_ptr<myClass> myClassObject(new myClass());
następnie chciałem przekazać to jako argument do metody:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
Czy powyższe po prostu zwiększa liczbę referencji shared_pt i wszystko jest w porządku? A może pozostawia zwisający wskaźnik?
Nadal masz to zrobić ?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Myślę, że drugi sposób może być bardziej wydajny, ponieważ musi skopiować tylko 1 adres (w przeciwieństwie do całego inteligentnego wskaźnika), ale pierwszy sposób wydaje się bardziej czytelny i nie przewiduję przesuwania granic wydajności. Chcę się tylko upewnić, że nie ma w tym nic niebezpiecznego.
Dziękuję Ci.
c++
c++11
shared-ptr
c++-faq
Steve H.
źródło
źródło
const std::shared_ptr<myClass>& arg1
DoSomething
naprawdę trzeba dzielić się własnością? Wygląda na to, że zamiast tego należy użyć odniesienia ...void DoSomething(myClass& arg1)
.shared_ptr<>
konkretnie, musisz przekazać wartość, aby faktycznie być współwłasnością.std::make_shared
jest nie tylko bardziej wydajne, ale także bezpieczniejsze niżstd::shared_ptr
konstruktor.Odpowiedzi:
Jasne, mogę ci w tym pomóc. Zakładam, że rozumiesz semantykę własności w C ++. Czy to prawda?
Dobry.
Ok, przychodzą mi do głowy tylko dwa powody, dla których warto się
shared_ptr
kłócić:shared_ptr
s.Który Cię interesuje?
Przykładami takich funkcji są
std::static_pointer_cast
komparatory niestandardowe lub predykaty. Na przykład, jeśli chcesz znaleźć wszystkie unikalne shared_ptr z wektora, potrzebujesz takiego predykatu.Dokładnie.
Tak. A jeśli to nie zmieni wskaźnika, chcesz przejść przez odwołanie do stałej. Nie ma potrzeby kopiowania, ponieważ nie musisz dzielić się własnością. To jest inny scenariusz.
Ten, w którym jesteś współwłasnością? Ok. W jaki sposób dzielisz się własnością
shared_ptr
?Wtedy funkcja będzie musiała wykonać kopię
shared_ptr
, prawda?Nie, to pesymizacja. Jeśli zostanie przekazany przez odniesienie, funkcja nie będzie miała innego wyboru, jak tylko wykonać kopię ręcznie. Jeśli zostanie przekazana przez wartość, kompilator wybierze najlepszy wybór między kopiowaniem a przeniesieniem i wykona go automatycznie. Więc podaj wartość.
Funkcja może po prostu przenieść
shared_ptr
argument do swojej pamięci. Przeniesienie ashared_ptr
jest tanie, ponieważ nie zmienia liczby odniesień.W takim przypadku
shared_ptr
jest całkowicie bez znaczenia dla funkcji. Jeśli chcesz manipulować wskazanym, weź go i pozwól dzwoniącym wybrać, jakiej semantyki własności chcą.Obowiązują standardowe zasady. Inteligentne wskazówki niczego nie zmieniają.
Dobrze.
Ach, ciekawy skrajny przypadek. Nie spodziewam się, że zdarzy się to często. Ale kiedy to się stanie, możesz albo przekazać wartość i zignorować kopię, jeśli jej nie potrzebujesz, albo przekazać przez odniesienie i utworzyć kopię, jeśli jej potrzebujesz.
Jeśli jesteś w sytuacji, w której to naprawdę ma znaczenie, możesz zapewnić dwa przeciążenia, jedno pobierające odwołanie do stałej lwartości, a drugie pobierające odwołanie do wartości r. Jedna kopia, druga się porusza. Szablon funkcji doskonałego przekazywania to kolejna opcja.
źródło
shared_ptr<T> ptr;
(tj. Wywołanovoid f(T&)
zf(*ptr)
), a obiekt nie przeżywa połączenia. Ewentualnie napisz taki, w którym omijasz wartościvoid f(T)
i napotkane problemy.void f(T&)
i dotyczy wątków. Myślę, że moim problemem jest to, że ton Twojej odpowiedzi zniechęca do przekazywania własności shared_ptr, co jest najbezpieczniejszym, najbardziej intuicyjnym i najmniej skomplikowanym sposobem ich używania. Byłbym zdecydowanie przeciwny każdemu początkującemu, aby wyodrębnił odniesienie do danych należących do shared_ptr <>. Możliwości niewłaściwego użycia są ogromne, a korzyści minimalne.Myślę, że ludzie niepotrzebnie boją się używania surowych wskaźników jako parametrów funkcji. Jeśli funkcja nie będzie przechowywać wskaźnika lub w inny sposób wpływać na jego żywotność, surowy wskaźnik działa równie dobrze i reprezentuje najniższy wspólny mianownik. Zastanów się na przykład, jak przekazać a
unique_ptr
do funkcji, która przyjmujeshared_ptr
parametr a jako parametr, według wartości lub przez odwołanie do stałej?void DoSomething(myClass * p); DoSomething(myClass_shared_ptr.get()); DoSomething(myClass_unique_ptr.get());
Surowy wskaźnik jako parametr funkcji nie zapobiega używaniu inteligentnych wskaźników w kodzie wywołującym, gdzie jest to naprawdę ważne.
źródło
DoSomething(*a_smart_ptr)
fun(&x)
dofun(x)
. W tym drugim przykładziex
może być przekazana przez wartość lub jako stała ref. Jak wspomniano powyżej, pozwoli ci to również przejść,nullptr
jeśli nie jesteś zainteresowany wyjściem ...Tak, cała idea shared_ptr <> polega na tym, że wiele instancji może przechowywać ten sam surowy wskaźnik, a podstawowa pamięć zostanie zwolniona tylko wtedy, gdy ostatnia instancja shared_ptr <> zostanie zniszczona.
Unikałbym wskaźnika do shared_ptr <>, ponieważ jest to sprzeczne z celem, ponieważ teraz znowu masz do czynienia z raw_pointers.
źródło
Przekazywanie wartości w pierwszym przykładzie jest bezpieczne, ale jest lepszy idiom. Jeśli to możliwe, przekazuj przez odniesienie const - powiedziałbym, że tak, nawet w przypadku inteligentnych wskaźników. Twój drugi przykład nie jest całkiem zepsuty, ale jest bardzo
!???
. Głupie, nie osiągając niczego i pokonując część sensu inteligentnych wskazówek, i zostawiając cię w powolnym świecie bólu, gdy próbujesz wyłuskać i zmodyfikować rzeczy.źródło
shared_ptr<>
szczególności o to, że musisz wykonać kopię, aby faktycznie być współwłasnością; jeśli masz odniesienie lub wskaźnik do a,shared_ptr<>
to nie udostępniasz niczego i podlega tym samym problemom związanym z czasem życia, co normalne odniesienie lub wskaźnik.shared_ptr
obiektu wywołującego ma gwarancję, że będzie trwał dłużej niż wywołanie funkcji, więc funkcja jest bezpieczna w użyciushared_ptr<> const &
. Jeśli zajdzie potrzeba przechowywania wskaźnika na później, będzie musiał skopiować (w tym momencie należy wykonać kopię, zamiast przechowywać odniesienie), ale nie ma potrzeby ponoszenia kosztów kopiowania, chyba że musisz to zrobić to. W tym przypadku chciałbym przekazać stałe referencje ....shared_ptr
z interfejsu i przekazać zwykłą (const
) referencję do wskazanego obiektu.shared_ptr<>
na początku? Teraz twoje API jest naprawdę uciążliwe, ponieważ zmusza wywołującego do bezcelowej zmiany semantyki czasu życia obiektu tylko po to, aby go użyć. Nie widzę w tym nic wartego poparcia poza stwierdzeniem, że „technicznie nic to nie szkodzi”. Nie widzę nic kontrowersyjnego w kwestii „jeśli nie potrzebujesz współwłasności, nie używajshared_ptr<>
”.w funkcji
DoSomething
zmieniasz element członkowski danych instancji klasy,myClass
więc modyfikujesz obiekt zarządzany (nieprzetworzony wskaźnik), a nie obiekt (shared_ptr). Oznacza to, że w punkcie powrotu tej funkcji wszystkie współdzielone wskaźniki do zarządzanego wskaźnika surowego zobaczą swój element członkowski danych:myClass::someField
zmieniony na inną wartość.w tym przypadku przekazujesz obiekt funkcji z gwarancją, że jej nie modyfikujesz (mówimy o shared_ptr, a nie o posiadanym obiekcie).
Idiomem, który to wyraża, jest: a const ref, tak jak to
void DoSomething(const std::shared_ptr<myClass>& arg)
Podobnie zapewniasz użytkownika swojej funkcji, że nie dodajesz kolejnego właściciela do listy właścicieli surowego wskaźnika. Jednak zachowałeś możliwość modyfikacji podstawowego obiektu wskazywanego przez surowy wskaźnik.
CAVEAT: Co oznacza, że jeśli w jakiś sposób ktoś
shared_ptr::reset
wywoła przed wywołaniem Twojej funkcji, aw tym momencie jest to ostatni shared_ptr będący właścicielem raw_ptr, to twój obiekt zostanie zniszczony, a twoja funkcja będzie manipulować wiszącym wskaźnikiem na zniszczonym obiekcie. BARDZO NIEBEZPIECZNE!!!źródło