shared_ptr pointer create c++ c++11 shared-ptr weak-ptr

c++ - pointer - shared_ptr reference



Equality-compare std:: weak_ptr (1)

Reescribiendo completamente esta respuesta porque entendí mal. Esto es una cosa difícil de hacer bien!

La implementación habitual de std::weak_ptr y std::shared_ptr que es coherente con el estándar es tener dos objetos de montón: el objeto gestionado y un bloque de control . Cada puntero compartido que se refiere al mismo objeto contiene un puntero al objeto y al bloque de control, y también a cada puntero débil. El bloque de control mantiene un registro del número de punteros compartidos y el número de punteros débiles, y desasigna el objeto gestionado cuando el número de punteros compartidos alcanza 0; el bloque de control en sí se desasigna cuando el número de punteros débiles también llega a 0.

Esto se complica por el hecho de que el puntero del objeto en un puntero débil o compartido puede apuntar a un subobjeto del objeto administrado real, por ejemplo, una clase base, un miembro o incluso otro objeto de montón que es propiedad del objeto administrado.

S0 ----------______ MO <------+ /__ `----> BC | /_ _______--------> m1 | ___X__ m2 --> H | S1 -/ /__ __----------------^ | /___ _____X__ | ____X________/__ | W0 /----------------`---> CB -------+ s = 2 w = 1

Aquí tenemos dos punteros compartidos que apuntan respectivamente a una clase base del objeto gestionado y a un miembro, y un puntero débil que apunta a un objeto de montón que pertenece al objeto administrado; el bloque de control registra que existen dos punteros compartidos y un puntero débil. El bloque de control también tiene un puntero al objeto administrado, que utiliza para eliminar el objeto administrado cuando caduca.

La semántica de owner_before / owner_less consiste en comparar los punteros débiles y compartidos por la dirección de su bloque de control, que se garantiza que no cambiará a menos que se modifique el puntero; incluso si un puntero débil caduca porque todos los punteros compartidos se han destruido, su bloque de control todavía existe hasta que todos los punteros débiles también se hayan destruido.

Por lo tanto, su código equals es absolutamente correcto y seguro para hilos.

El problema es que no es coherente con shared_ptr::operator== porque compara los punteros de los objetos, y dos punteros compartidos con el mismo bloque de control pueden apuntar a objetos diferentes (como anteriormente).

Para shared_ptr::operator== coherencia con shared_ptr::operator== , escribir t.lock() == u estará absolutamente bien; sin embargo, tenga en cuenta que si devuelve true entonces aún no es definitivo que el puntero débil sea un puntero débil del otro puntero compartido; podría ser un puntero de alias y, por lo tanto, podría caducar en el siguiente código.

Sin embargo, comparar bloques de control tiene menos sobrecarga (porque no es necesario mirar el bloque de control) y dará los mismos resultados que == si no está utilizando punteros de alias.

Creo que hay algo de una deficiencia en el estándar aquí; agregar un owner_equals y owner_hash permitiría el uso de weak_ptr en contenedores desordenados, y dados los owner_equals en realidad es sensato comparar los punteros débiles para la igualdad, ya que puede comparar de forma segura el puntero del bloque de control y luego el puntero del objeto, ya que si dos punteros débiles tienen el mismo control Bloquea entonces sabes que ambos o ninguno están vencidos. Algo para la próxima versión del estándar, tal vez.

Quiero comparar dos std :: weak_ptr''s o una std :: weak_ptr y una std :: shared_ptr para la igualdad.

Lo que quiero saber es si el objeto al que apunta el punto debilidad / valor_ptr_de_trabajo es el mismo. La comparación debe producir resultados negativos no solo si las direcciones no coinciden, sino también si el objeto subyacente se eliminó y luego se reconstruyó con la misma dirección por casualidad.

Básicamente, quiero que esta afirmación se mantenga incluso si el asignador reserva la misma dirección:

auto s1 = std::make_shared<int>(43); std::weak_ptr<int> w1(s1); s1.reset(); auto s2 = std::make_shared<int>(41); std::weak_ptr<int> w2(s2); assert(!equals(w1,w2));

Las plantillas weak_ptr no proporcionan operadores de igualdad, y como he entendido, eso es por una buena razón .

Así que una implementación ingenua se vería así:

template <typename T, typename U> inline bool naive_equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u) { return !t.expired() && t.lock() == u.lock(); } template <typename T, typename U> inline bool naive_equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u) { return !t.expired() && t.lock() == u; }

Si, mientras tanto, el primer weak_ptr expiró, arroja 0. Si no, actualizo el valor de weak_ptr a shared_ptr y comparo las direcciones.

¡El problema con esto es que tengo que bloquear la debilidad de dos veces (una vez)! Me temo que eso lleva mucho tiempo.

Se me ocurrió esto:

template <typename T, typename U> inline bool equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u) { return !t.owner_before(u) && !u.owner_before(t); } template <typename T, typename U> inline bool equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u) { return !t.owner_before(u) && !u.owner_before(t); }

Lo que comprueba si el bloque propietario de u no es "antes" de t y t no es anterior a u, por lo que t == u.

¿Funciona esto como lo pretendo? ¿Los dos puntos débiles creados a partir de distintos compartimientos compartidos se comparan como no iguales de esta manera? ¿O me perdí algo?

Edit: ¿Por qué quiero hacer esto en primer lugar? Quiero tener un contenedor con punteros compartidos y quiero entregar referencias a los objetos que contiene. No puedo usar iteradores, ya que pueden ser invalidados. Podría entregar los ID (enteros), pero eso conduce a problemas con la singularidad y requeriría un tipo de mapa y complicar las operaciones de búsqueda / inserción / eliminación. La idea es usar un std :: set y distribuir los punteros en sí mismos (encapsulados en una clase de envoltorio) como claves, de modo que los clientes puedan usar la debilidad de puntos de acceso para acceder a los objetos del conjunto.