2015-12-06 15 views
16

Obecnie używam niektórych funkcji z biblioteki glib. Z glibem przychodzi również gio. glib jest biblioteką C i dlatego muszę usunąć niektóre struktury, które tworzę.Jak używać shared_ptr ze wskaźnikiem do struct, który nie powinien zostać zwolniony

dla wielu obiektów tworzę sprytny wskaźnik np

std::shared_ptr<GAsyncQueue> my_queue = std::shared_ptr<GAsyncQueue>(g_async_queue_create(), g_async_queue_unref); 

W tym celu tworzy wspólny wskaźnik do GAsyncQueue i jest bezpiecznie niszczy kolejkę na jego koniec jego życia.

Jednak pojawia się problem, gdy otrzymam wskaźnik z biblioteki gio, którego nie powinienem zwolnić. W poniższym kodzie my_connection jest GSocketClient, który implementuje (w glib speak) GIOStream.

std::shared_ptr<GInputStream> my_input_stream = 
    std::shared_ptr<GInputStream> (
     g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())) 
    ); 

Ponieważ dokumentacja GIOStream wspomina, że ​​wskaźnik uzyskano g_io_stream_get_input_stream() nie powinna być zwolniona. Jest tak dlatego, że jest własnością instancji my_connection. Pomyślałem o stworzeniu lamdy dla obiektu zniszczonego, drugiego parametru obiektu współdzielonego wskaźnika. np. auto deleter = [](GInputStream* ptr) {};, a następnie nadaj tej funkcji lambdę funkcję detrozy we współużytkowanej wskazówce, ale to wydaje się głupie.

+3

Dlaczego warto używać wskaźnika (smart)? Czy odniesienie nie wystarczy? – edmz

+0

@ black Wciąż trochę się nad tym zastanawiam. Strumień wejściowy jest instancją obiektu, który jest kopiowany. GIOStream ulega zniszczeniu po wywołaniu ostatniego destruktora kopii. Być może, ponieważ i tak nie muszę go zniszczyć, jest to prosta wskazówka również w porządku ... – hetepeperfan

+0

I wskaźnik, który mogę ustawić na NULL, wtedy łatwiej jest sprawdzić, czy jest on zainicjalizowany. – hetepeperfan

Odpowiedz

12

Cóż, alternatywą dla nie-op Deleter może być używany aliasing wspólny wskaźnik

template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept; 

Dzieli x, ale po get() dostaniesz z powrotem p.

Dyskusja: What is shared_ptr's aliasing constructor for?

5

Można użyć typu Deleter, że nic nie robi, ale to musi być przekazany jako argument do konstruktora shared_ptr „s

struct DoNothing { 
    template <typename T> 
    void operator()(T*) const noexcept { } 
}; 

Tworząc shared_ptr trzeba będzie utworzyć jedną z tych deleters i przekazać go do konstruktora (tak jak w przypadku lambda). Można to ułatwić na siebie z pośrednim funkcji

template <typename T> 
std::shared_ptr<T> non_deleting_shared_ptr(T* ptr) { 
    return {ptr, DoNothing}; 
} 

auto my_input_stream = 
    non_deleting_shared_ptr(
     g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get())); 

Jednak większy pytanie, dlaczego używasz inteligentne kursory, gdy nie chcesz własność być jego częścią. Prawie na pewno lepiej byłoby, gdybyś miał tylko GAsyncQueue*, chyba że jesteś w sytuacji, w której masz shared_ptr, który musi zwolnić czasami. Może jak członek danych?

6

Prawdopodobnie po prostu nie potrzebują std::shared_ptr. Prawdopodobnie nie potrzebujesz nawet wskaźnika.

Jak czytam twoje pytanie i komentarze, nie widzę żadnego punktu przeciwko

auto& my_input_stream = *(g_io_stream_get_input_stream(G_IO_STREAM(my_connection.get()))) 

Prawdą jest, że wskaźniki pozwalają danych opcjonalnych. Jednak prawdą jest również, że najczęściej używa się go w niewłaściwy sposób. Posiadanie często nie ma sensu. Jeśli funkcja musi działać na konkretnych danych, dopuszczenie parametru NULL jest przydatne tylko wtedy, gdy wtedy martwisz się o dostarczenie tych danych.W przeciwnym razie wystarczy odwołać się do obiektu (np. const).

Inteligentne wskaźniki są użyteczne; ale wciąż są wskazówkami. Unikanie ich w ogóle, jeśli to możliwe, jest jeszcze lepsze.


Z uwag:

Jednak odniesienie zawsze musi być zainicjowany

Absolutnie. Od C++ 11, ale mamy std::reference_wrapper, który można również ponownie zapisać i przechowywać w pojemnikach.

+0

Na podstawie mojego pytania masz rację. Jednak zawsze należy zainicjować odniesienie. Ponieważ my_input_stream jest członkiem klasy (który nie był wyświetlany w moim pytaniu), który nie jest otwarty. Mogę ustawić to tylko wtedy, gdy faktycznie otworzę połączenie z gniazdem. Ale zrobię to, ponieważ jest to dobra odpowiedź dla innych czytelników. – hetepeperfan

+0

@hetepeperfan True. Sprawdź, czy moja aktualizacja może rozwiązać ten problem;) – edmz