23

En otras palabras, ¿cómo realiza la implementación el conteo?¿Cómo funciona el recuento de referencias de un puntero inteligente de recuento de referencias?

¿Hay un objeto tipo mapa mantenido que sea accesible por todas las instancias shared_ptr cuya clave es la dirección del puntero y el valor es el número de referencias? Si tengo que implementar un shared_ptr, esta es la primera idea que viene a mi mente.

¿Existe la posibilidad de una pérdida de memoria en el caso de estos indicadores inteligentes de recuento de referencias? Si es así, ¿cómo puedo evitarlos?

Respuesta

58

que he visto dos enfoques no intrusivos diferentes a esto:

  1. El puntero inteligente asigna una pequeña bloque de memoria para contener el contador referencia. Cada copia del puntero inteligente recibe un puntero para el objeto real y un puntero para el recuento de referencia.
  2. Además de un puntero de objeto, cada puntero inteligente contiene un puntero anterior y siguiente , de este modo la formación de una lista doblemente enlazada de punteros inteligentes a un objeto particular. El recuento de referencia es implícito en la lista. Cuando se copia un puntero inteligente , se agrega a la lista . Tras la destrucción, cada puntero inteligente se elimina de la lista de . Si es el último en , la lista también libera el objeto referenciado .

Si va here y se desplaza hacia la parte inferior, hay un excelente diagrama que explica estos métodos con mucha más claridad.

+0

Esta es la respuesta más correcta. –

+2

El enfoque de lista enlazada evita la asignación adicional, pero es muy difícil hacer "thread-safe" sin un mutex global. ("thread-safe" como en "como hilo seguro como un puntero sin formato") – curiousguy

+2

Además, si usa 'make_shared', también puede evitar la asignación adicional colocando el objeto asignado y el contador de instancia en un solo bloque de memoria. – Ferruccio

2

No. shared_ptr simplemente mantenga un puntero adicional para el recuento de referencias.

Cuando realiza una copia del objeto shared_ptr, copia el puntero con el recuento de referencias, lo aumenta y copia el puntero en el objeto contenido.

2

Crear una pérdida de memoria con punteros inteligentes de recuento de referencias es muy fácil. Simplemente cree cualquier estructura tipo gráfica de objetos que tenga un ciclo en el gráfico. Los objetos en el ciclo se evitarán mutuamente. Esto no puede resolverse automáticamente; por ejemplo, cuando crea una lista de enlaces dobles, debe tener cuidado de no eliminar nunca más de un objeto a la vez.

3

Cada objeto de puntero inteligente contiene un recuento de referencias compartidas, una por cada puntero sin formato.

Puede echar un vistazo al artículo this. Esta implementación almacena estos en un objeto separado que se copia. También puede echar un vistazo a boost's documentation o echar un vistazo al Wikipedia article en punteros inteligentes.

+0

-1. Todos los punteros inteligentes que se refieren al mismo objeto deben * compartir * un solo recuento de referencia. El primer objeto del puntero inteligente asigna el objeto que contiene el recuento de referencias, y * los punteros a él * (NO el objeto mismo) se copian cuando se copia el puntero inteligente. –

+0

Estoy de acuerdo en j_random_hacker. El recuento es único para cada puntero sin formato y compartido por todos los shared_ptr que se refieren al mismo puntero sin formato. Por lo general, se asigna como un trozo de memoria separado por lo que smart_ptr tiene dos ptrs internos, uno para el recuento de referencias y otro para el puntero. –

+0

-1 para variable estática. A menos que esté implementando un puntero inteligente contado por referencia a un objeto singleton, no puede usar estadísticas para implementar el recuento de referencias. –

2

Por lo que recuerdo, existía el problema del puntero de conteo de referencia tratado en un capítulo de C++ efectivo.

En principio, tiene la clase de puntero "ligero", que contiene un puntero a una clase que contiene la referencia que sabe incrementar/disminuir la referencia y destruir el objeto puntero. Esa clase de conteo de referencia apunta al objeto que se va a referenciar.

2

Muchas respuestas abordan la forma en que se almacena el recuento de referencias (se almacena en una memoria compartida para todas las shared_ptr que contienen el mismo puntero nativo), pero la mayoría elude el problema de las fugas.

La manera más fácil de filtrar la memoria con los punteros contados de referencia es crear ciclos. Como ejemplo, una lista doblemente enlazada donde todos los punteros son shared_ptr con al menos dos elementos se garantiza que no se eliminará. Incluso si los punteros externos se liberan, los punteros internos seguirán contando, y el recuento de referencias no alcanzará 0. Es decir, al menos, con la implementación más ingenua.

La solución más fácil para el problema del ciclo es la mezcla de shared_ptr (punteros de referencia contados) con punteros débiles que no comparten la propiedad del objeto.

Los punteros compartidos compartirán tanto el recurso (puntero) como la información adicional de la referencia_cuenta. Cuando utiliza punteros débiles, el recuento de referencias se duplica: hay un recuento de referencia de puntero compartido y un recuento de referencia de puntero débil. El recurso se libera siempre que el conteo del puntero compartido llega a 0, pero la información reference_count se deja con vida hasta que se lanza el último puntero débil.

En la lista de doble enlace, la referencia externa se lleva a cabo en un shared_ptr, mientras que los enlaces internos son solo weak_ptr. Cuando no hay referencias externas (shared_ptr) se liberan los elementos de la lista, eliminando las referencias débiles. Al final, todas las referencias débiles se han eliminado y el último puntero débil de cada recurso libera la información reference_count.

Es menos confuso que el texto anterior parece ... Lo intentaré más tarde.

+2

No lo intentó más tarde. – xaxxon

Cuestiones relacionadas