2012-08-17 12 views
5

¿Es el siguiente un enfoque razonable y eficiente con respecto a la creación de material y dando a Foo la propiedad de él?Pasando std :: shared_ptr a Constructores

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 typo – Baz

+6

En realidad, 'Foo' no * se apropia *. El objetivo de 'shared_ptr' es * compartir * propiedad. – juanchopanza

+0

O prefiera 'std :: shared_ptr foosStuff (new Stuff());' – stefaanv

Respuesta

16

Puesto que usted está interesado en la eficiencia Me gustaría hacer dos observaciones:

shared_ptr <> es uno de los muchos tipos de bibliotecas estándar, donde una construcción movimiento es más barato que una construcción copia. Copiar la construcción de shared_ptr es más lento ya que la copia requiere que los contadores de referencia se incrementen atómicamente mientras que mover un shared_ptr no requiere tocar los datos o contadores referenciados. Del artículo "Want Speed? Pass by value!" de Dave Abrahams uno puede aprender que en ciertas situaciones, en realidad es beneficioso tomar un parámetro de función por valor. Este es uno de estos casos:

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

    ... 

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

Ahora usted puede escribir

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

donde el argumento es un temporal y sin shared_ptr es cada vez copiada (acaba de mudar una o dos veces).

El uso de std :: make_shared aquí tiene la ventaja de que solo se realiza una asignación. En su caso, usted asignó el objeto Stuff usted mismo y el constructor shared_ptr también tuvo que asignar los contadores de referencia y el eliminador dinámicamente. make_shared lo hace todo por ti con solo una asignación.

+1

Me gusta esa función make_shared – Baz

+1

por cierto: Si cambia a std :: unique_ptr, en realidad _HAVE_ pasa por valor;) – sellibitze

+0

@sellibitze ¿Cuál es el beneficio de pasar la clase a shared_ptr en su constructor? si no vas a quedártelo. Seguramente en este caso, es mejor pasar un unique_ptr y hacer que la clase construya su shared_ptr a partir de eso. – jleahy

4

Sí, eso es perfectamente razonable. De esta forma, el trabajo involucrado en la administración del puntero compartido solo tendrá que hacerse una vez, en lugar de dos veces si lo pasó por valor. También podría considerar usar make_shared para evitar una construcción de copias.

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

La única mejora que podría hacer es que si Foo es ser el único propietario (es decir. No va a mantener foosStuff alrededor después de la creación de Foo), entonces podría pasar a utilizar un std :: unique_ptr o boost :: scoped_ptr (que es el equivalente previo a C++ 11), que tendría menos sobrecarga.

+0

Bien, debería buscar más información sobre otros punteros inteligentes, ya que solo estoy familiarizado con shared_ptr. – Baz

+0

¿Qué es 'scoped_ptr', no es parte de C++? Tal vez te refieres a 'std :: unique_ptr'? ¿Y por qué el eliminador personalizado? –

+0

@ChristianRau: Supongo que lo que significa es 'boost :: scope_ptr'. ¿El bit de eliminación puede ser una referencia obsoleta a una edición anterior (sigilosa)? –

3

Podría ser más eficiente tener un ayudante make_foo:

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

Ahora usted puede decir auto f = make_foo();. O al menos use la invocación make_shared usted mismo, ya que el resultante shared_ptr puede ser más eficiente que el construido a partir de una expresión new. Y si Stuff realmente tiene argumentos de constructor, un constructor auxiliar privado podría ser adecuado:

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 
    { } 
}; 

Usted puede envolver Foo::make en el anterior make_foo, o usarlo directamente:

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

Dicho esto, a menos que Realmente es compartir propiedad, un std::unique_ptr<Stuff> suena como un enfoque más preferible: es conceptualmente mucho más simple y también algo más eficiente. En ese caso, diría m_myStuff(new Stuff(std::forward<Args>(args)...)) en la línea marcada #1.

+0

+1 por mencionar unique_ptr :) – sellibitze

Cuestiones relacionadas