2012-12-14 14 views
10

Gdybym mógł, usunąłbym wszystkie surowe wskaźniki * z mojego kodu, ponieważ ich używanie może nie być bezpieczne dla wątków, a intencje projektu nie są jasne (wartość opcjonalna, własność, itp.) . Czasami jednak nie jest łatwo nie używać wskaźników. Na przykład mamy tendencję do używania wskaźników dla typu podstawowego w kontenerze typów polimorficznych:std :: reference_wrapper <T> użycie w kontenerze

class A : noncopyable { ... }; 
class B : public A { ... }; 

std::vector<A*> v; 
v.emplace_back(new B); 

// temporary container for some operation 
std::vector<A*> selected; 
if(check()) 
    selected.emplace_back(v.front()); 

Co możesz powiedzieć o powyższym kodzie? Kto jest właścicielem? Czy jest to własność współdzielona, ​​czy nie? To dlaczego my prawdopodobnie zrobić dla v:

std::vector<std::unique_ptr<A>> v; 
v.emplace_back(make_unique<B>()); 

Teraz jest jasne, że v jest właścicielem obiektów, ale nadal nie tak selected ma wskaźnik surowego i sprawia, że ​​mój projekt nie intuicyjne. Patrząc w bibliotece standard C++ myślę, że istnieje tylko jeden typ, który mógłby wykonać zadanie - std::reference_wrapper:

std::vector<std::unique_ptr<A>> v; 
v.emplace_back(make_unique<B>()); 

// temporary container for some operation 
std::vector<std::reference_wrapper<A>> selected; 
if(check()) 
    selected.emplace_back(*v.front()); 

Jak się czujesz o tym kodem? Czy to dobra praktyka? Wiem, że std::ref() i std::cref, gdzie ma przede wszystkim pracować z szablonami, ale wydaje się, że tutaj możemy również użyć go do wyraźnego określenia naszego zamiaru projektowego. Jedyny problem, jaki widzę, to to, że muszę usunąć dereferencję std::reference_wrapper z get() i nie ma w środku operator*() ani operator->(), aby mieć taki sam interfejs jak w kontenerze z unique_ptr. Czy powinienem napisać coś podobnego na własną rękę? A może referencyjny_wrapper mógłby zostać rozszerzony na taki przypadek użycia w przyszłych wersjach C++? Podziel się swoją opinią.

EDYCJA: Zmieniłem próbki kodu, aby lepiej pokazać zamiar.

+2

Jaki jest czas życia "wybranego"? Czy jest zagwarantowane, że zostanie zniszczony (lub opróżniony) przed obiektami, na które punkt jego elementów zostanie zniszczony? Jeśli tak, to użyj surowych wskaźników: nie ma potrzeby współdzielenia własności. Nie ma nic złego w używaniu surowych wskaźników w C++; po prostu upewnij się, że surowe wskazówki nie wskazują na obiekt wskazany. –

+2

Wygląda na to, że są one 'shared_ptr''s. – perreal

+0

@JamesMcNellis Masz rację. Wydaje mi się, że z takiego małego urywka jest jasne, do czego służą "wybrane" i "v". Jednak w większym zakresie/klasie nie jest łatwo dostrzec związki w projekcie. W niektórych metodach zaczynasz pracę nad 'selected' tylko bez widoku na' v' i zaczynasz się zastanawiać, co oznacza ten wskaźnik. Czy jest to wartość opcjonalna, własność lub po prostu wskaźnik klasy bazowej? Szukam ładnego sposobu na przedstawienie mojej intencji projektu bez kosztów ogólnych w czasie wykonywania. –

Odpowiedz

5

Dostarczyłeś już rozwiązanie, które wygląda na dźwięk. Rozumiem, że pytanie brzmi "Jak się czujesz?"

Moje osobiste odczucie polega na tym, że istnieje potrzeba zachowania równowagi pomiędzy bezpieczeństwem i jednoznacznością z jednej strony a prostotą kodu z drugiej. Wygląda na to, że twoje rozwiązanie może zbyt mocno naciskać na bezpieczeństwo i zbytnio obniżać prostotę. Ilekroć używałem pojemników z "słabymi referencjami", użyłem surowych wskaźników do ich przedstawienia. To prawda, że ​​może to mniej wyjaśniać, kto jest właścicielem obiektu, ale ma też pewne zalety: nie musisz się uczyć, czym jest "reference_wrapper", a kod jest czysty. Jeśli ich używasz (kontener słabych referencji) tylko tymczasowo i zamkniesz to użycie, problem własności powinien być minimalny.

Ale to tylko kwestia osobistych preferencji. Pozwolę sobie tylko zaproponować różne typy w tym samym celu.Jest to przewidziane, że możesz pozwolić sobie na użycie Boost. W przypadku "silnych" odniesień (do których należy zasób) możesz użyć biblioteki Steve'a Watanabe'a: Type Erasure. Nie wymaga jawnego korzystania z pamięci wolnej pamięci i przypuszczam, że dla małych typów może całkowicie zrezygnować z korzystania z pamięci sterty (przy użyciu optymalizacji małych buforów). Niedawno został zaakceptowany przez Boost, chociaż nie został jeszcze wydany.

dla słabych referencji, należy rozważyć użycie "opcjonalne" referencje z Boost.Optional:

int i = 0; 
boost::optional<int&> oi = i; // note: int& 
i = 2; 
assert(*oi == 2); 

Ma samą semantykę jak reference_wrapper.

0

Myślę, że wywołanie ich shared_ptr nie jest logicznie błędne. Jednak patrząc na definicję std::weak_ptr:

std :: weak_ptr jest inteligentny wskaźnik, który posiada nie posiadającą („słaby”) odniesienie do obiektu, który jest zarządzany przez std :: shared_ptr. Musi być przekonwertowany na std :: shared_ptr, aby uzyskać dostęp do przywoływanego obiektu.

To może być lepszy kandydat. Przynajmniej wtedy, gdy manipulujesz wskaźnikiem przez selected, musisz założyć tymczasową własność. Ponieważ oryginalny wskaźnik jest przechowywany we współużytkowanym wskaźniku, użycie słabego wskaźnika będzie bezpieczniejsze.

+1

Hmm, to by działało. Jednak nie podoba mi się nakład pracy i pamięci na 'std :: shared_ptr'. Używam ich tylko wtedy, gdy naprawdę ich potrzebuję -> własność współdzielona. –

+0

Można argumentować, że w pewnym momencie dzielisz własność. W przeciwnym razie używaj tylko wskazanych wskaźników. – perreal

+1

@MateuszPusz Nie należy zapominać o * "** obciążeniu pojęciowym **" * własności współdzielonej, gdy jest używana do unikatowego właściciela. –

Powiązane problemy