2012-08-17 16 views
5

Czy rozsądne i skuteczne podejście do tworzenia rzeczy i nadania Foo własności jest następujące:Przekazywanie std :: shared_ptr do konstruktorów

class Foo 
{ 
    explicit Foo(const std::shared_ptr<Stuff>& myStuff) 
     : m_myStuff(myStuff) 
    { 
    } 

    ... 

private: 
    const std::shared_ptr<Stuff> m_myStuff; 
} 

std::shared_ptr<Stuff> foosStuff(new Stuff()); 
Foo f(foosStuff); 
+0

@sellibitze litero – Baz

+6

Niezupełnie, 'Foo' nie * bierze * własność. Celem 'shared_ptr' jest * share * ownership. – juanchopanza

+0

Lub wolisz 'std :: shared_ptr foosStuff (new Stuff());' – stefaanv

Odpowiedz

16

Ponieważ jesteś zainteresowany efektywności Chciałbym zrobić dwa punkty:

shared_ptr <> jest jednym z wielu standardowych typów bibliotek, gdzie budowa ruch jest tańszy niż budowa kopii. Kopiowanie konstruowania shared_ptr jest wolniejsze, ponieważ kopiowanie wymaga, aby liczniki odwołań były inkrementowane inkrementalnie, podczas gdy przenoszenie shared_ptr nie wymaga w ogóle dotykania przywoływanych danych lub liczników. Z artykułu "Want Speed? Pass by value!" Dave'a Abrahamsa można dowiedzieć się, że w niektórych sytuacjach rzeczywiście korzystne jest przyjmowanie parametru funkcji według wartości. Jest to jeden z tych przypadków:

class Foo 
{ 
    explicit Foo(std::shared_ptr<Stuff> myStuff) 
    : m_myStuff(move(myStuff)) 
    {} 

    ... 

private: 
    std::shared_ptr<Stuff> m_myStuff; 
}; 

Teraz można napisać

Foo f (std::make_shared<Stuff>()); 

gdzie argument jest tymczasowy i nie shared_ptr jest zawsze kopiowane (po prostu przeniósł się raz lub dwa razy).

Użycie std :: make_shared ma tę zaletę, że wykonuje się tylko jedną alokację. W twoim przypadku sam przydzieliłeś obiekt Rzeczy, a konstruktor shared_ptr musiał również dynamicznie przydzielać liczniki odwołań i przenosniki. make_shared robi to wszystko za ciebie z pojedynczym przydziałem.

+1

Podoba mi się funkcja make_shared – Baz

+1

btw: Jeśli zmienisz na std :: unique_ptr, faktycznie _HAVE_, aby przekazać ją według wartości;) – sellibitze

+0

@sellibitze Jakie są korzyści z przekazania klasy shared_ptr w jej konstruktorze jeśli nie zamierzasz go zatrzymać. Z pewnością w tym przypadku lepiej jest przekazać w unique_ptr i skonstruować klasę shared_ptr z tego? – jleahy

4

Tak, to zupełnie uzasadnione. W ten sposób praca związana z zarządzaniem współużytkowanym wskaźnikiem będzie musiała zostać wykonana tylko raz, a nie dwa razy, jeśli przekazałeś ją według wartości. Możesz także rozważyć użycie make_shared, aby uniknąć konstrukcji kopii.

std::shared_ptr<Stuff> foosStuff(std::make_shared<Stuff>()); 

Jedynym poprawy można zrobić to czy Foo ma być jedynym właścicielem (tzn. Nie zamierzamy utrzymać foosStuff dookoła po utworzeniu Foo) a następnie można przełączyć się za pomocą std :: unique_ptr lub boost :: scoped_ptr (będący odpowiednikiem przed C++ 11), który miałby mniej narzutów.

+0

OK, powinienem popatrzeć, aby dowiedzieć się więcej o innych inteligentnych wskaźnikach, ponieważ jestem tylko zaznajomiony z shared_ptr. – Baz

+0

Co to jest 'scoped_ptr', nie jest częścią C++? Może masz na myśli 'std :: unique_ptr'? I dlaczego niestandardowy deleter? –

+0

@ChristianRau: Przypuszczam, że chodzi o 'boost :: scope_ptr'. Bit deletera może być przestarzałym odnośnikiem do wcześniejszej (ukrytej) edycji? –

3

To może być bardziej wydajny, aby mieć make_foo pomocnika:

Foo make_foo() { return Foo(std::make_shared<Stuff>()); } 

Teraz można powiedzieć auto f = make_foo();. Lub przynajmniej skorzystaj z wywołania make_shared samodzielnie, ponieważ wynikowy shared_ptr może być bardziej wydajny niż ten skonstruowany z wyrażenia new. A jeśli Stuff faktycznie ma argumenty konstruktora, prywatny konstruktor pomocniczy może być przydatny:

struct Foo 
{ 
    template <typename ...Args> 
    static Foo make(Args &&... args) 
    { 
     return Foo(direct_construct(), std::forward<Args>(args)...); 
    }; 

private: 

    struct direct_construct{}; 

    template <typeaname ...Args> 
    Foo(direct_construct, Args &&... args) 
    : m_myStuff(std::make_shared<Stuff>(std::forward<Args>(args)...)) // #1 
    { } 
}; 

Można też owinąć Foo::make do powyższego make_foo lub użyć go bezpośrednio:

auto f = Foo::make(true, 'x', Blue); 

To powiedziawszy, chyba że "Naprawdę udostępnianie własności, std::unique_ptr<Stuff> brzmi jak bardziej preferowane podejście: Jest zarówno koncepcyjnie znacznie prostsze, a także nieco bardziej wydajne. W takim przypadku można powiedzieć m_myStuff(new Stuff(std::forward<Args>(args)...)) w wierszu oznaczonym #1.

+0

+1 za wzmiankę unique_ptr :) – sellibitze

Powiązane problemy