2010-07-11 18 views
7

decir que tengo un std::list de class T 's:std :: lista <std :: unique_ptr <T>>: que pasa a su alrededor

std::list<T> l; 

Al pasar en funciones, me gustaría utilizar una referencia:

someFunction(std::list<T> &l) 

¿Cuál es la mejor manera de pasar (los elementos) de un std::list de unique_ptr s?

std::list< std::unique_ptr<T> > l; 

De esta manera:

someFunction(std::unique_ptr<T> ptr) 

O esto:

someFunction(T* ptr) 

O esto:

someFunction(T &ref) 

Y lo que ¿cómo iba a llamar usando el std::list 's back() función por ejemplo? Estos son en mi humilde opinión todo el "tipo de" equivalente, pero estoy seguro de que me falta algo aquí.

Gracias

Respuesta

7

Con el fin de mejor a peor:

  1. algunaFuncion (const T &);
  2. someFunction (T &);
  3. someFunction (const std :: unique_ptr <T> &);
  4. someFunction (std :: unique_ptr <T> &);

La primera es la mejor porque no modifica el objeto y funcionará con el objeto sin importar cómo lo haya asignado (por ejemplo, podría cambiar a shared_ptr sin problemas).

El número dos también funcionará independientemente de qué puntero inteligente esté utilizando; sin embargo, asume que puedes modificar el objeto, y siempre que puedas hacer algo const, deberías.

Los números 3 y 4 permiten que el objeto señalado sea mutado; sin embargo, # 3 no permite que se modifique el puntero inteligente, mientras que el número 4 sí lo hace. Ambos tienen la desventaja de que fuerzan el uso de unique_ptr, mientras que los dos anteriores funcionarían independientemente de la clase de puntero inteligente.

Al pasar un unique_ptr por valor, como lo ha hecho en algunos de los otros ejemplos, no es una opción; se supone que unique_ptr es único. Si está copiando, considere usar shared_ptr.

Para los dos primeros, si se invocó en el resultado de la espalda(), que se vería así:

someFunction(*(lst.back())); // dereference lst.back() before passing it in. 

Para los dos últimos, si lo invocó en la resut de la espalda(), que se vería así:

someFunction(lst.back()); // pass the smart pointer, not the object to 
          // which the smart pointer currently points. 
+0

Gracias por la explicación en profundidad. Por lo tanto, no es necesaria ninguna semántica especial para los punteros inteligentes (excepto la desreferenciación). Sabía sobre las cosas 'const', pero en este caso necesito modificar el objeto' T' que se ha pasado. – rubenvb

1

Haz no pase unique_ptr por valor, en primer lugar, que no se compilará sin std::move y si haces uso std::move se vaciará el valor que ha almacenado en su list y no podrá acceder a él más.

Esto se debe a unique_ptr no es copiable, que no tiene un constructor de copia de tipo unique_ptr::unique_ptr(const unique_ptr<T>& other) lugar que sólo tiene un constructor movimiento (unique_ptr::unique_ptr(unique_ptr<T>&& source)).

0

unique_ptr y también clases/instancias que contienen unique_ptr se puede utilizar en std :: lista (y otros contenedores), siempre que tengan movimiento constructorclass_name(class_name &&) define (que unique_ptr, por supuesto, tiene).

Cuando se pasa alrededor de esos elementos, que está siempre en movimiento (y no copiar) ellos, por lo que siempre utiliza std :: move() en lvalues, como en
my_list.push_back(std::move(my_element));
esto hace visible que eres pasando (= moviendo) el elemento a la lista, y que my_element está "vacío" (como empty_ptr vacío) después de esa operación.

Ejemplo:

typedef std::unique_ptr<uint8_t[]> data_ptr; 

class data_holder 
{ 
private: 
    int something_about_data; 
    data_ptr data; 
public: 
    data_holder(data_ptr && _data) 
     : data(std::move(_data)) 
    {} 

    // hey compiler, please generate a default move constructor for me 
    // despite of present destructor 
    data_holder(data_holder &&) = default; 

    ~data_holder() 
    { 
     // ... 
    } 

    bool is_empty() const { return ! bool(data); } 
} 

// ... 
{ 
    // ... 
    data_holder the_element(data_ptr(new uint8_t[DATA_SIZE])); 

    // move the_element into the_list 
    the_list.push_back(std::move(the_element)); 
    if (the_element.is_empty()) 
     std::cerr << "data_holder 'the_element' is now empty!" << std::endl; 
    // ... 
} 
Cuestiones relacionadas