Pregunta

He pasado los últimos 4 años en C #, así que estoy interesado en las mejores prácticas actuales y los patrones de diseño comunes en C ++. Consideremos el siguiente ejemplo parcial:

class World
{
public:
    void Add(Object *object);
    void Remove(Object *object);
    void Update();
}

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this;
        }
    }
}

Aquí tenemos un mundo responsable de la gestión de un conjunto de objetos y actualizar con regularidad. El fuego es un un objeto que puede añadirse al mundo en muchas circunstancias diferentes, pero por lo general por otro objeto que ya están en el mundo. El fuego es el único objeto que sabe cuando se ha quemado por lo que actualmente lo tengo eliminación de sí mismo. El objeto que creó el fuego es probable que ya no existen o relevante.

¿Es esto una cosa sensible a hacer o hay un mejor diseño que ayudaría a limpiar estos objetos?

¿Fue útil?

Solución

El problema con esto es que en realidad está creando un acoplamiento implícito entre el objeto y la clase mundial.

Si trato de llamar a Update () fuera de la clase mundial, lo que sucede? Yo podría terminar con el objeto de ser borrado, y no sé por qué. Parece que las responsabilidades están mal mezcladas. Esto va a causar problemas en el momento que utiliza la clase de incendio en una nueva situación en la que no había pensado cuando escribió este código. ¿Qué pasa si el objeto debe ser eliminado de más de un lugar? Tal vez se debe quitar tanto del mundo, el mapa actual, y el inventario del jugador? Su función de actualización se eliminará del mundo, y luego eliminar el objeto, y la próxima vez que el mapa o el inventario intenta acceder al objeto, pasan cosas malas.

En general, yo diría que es muy poco intuitivo para una función Update () para eliminar el objeto que se está actualizando. También me gustaría decir que es poco intuitivo para un objeto a borrarse a sí mismo. El objeto más probable debería tener algún tipo de manera de desencadenar un evento diciendo que ha terminado de quemarse, y cualquier persona interesada puede ahora actuar sobre eso. Por ejemplo quitándolo del mundo. Para eliminarlo, pensar en términos de propiedad.

A quién pertenece el objeto? ¿El mundo? Eso significa que el mundo solo puede decidir cuando el objeto muere. Eso está bien, siempre y cuando la referencia del mundo para el objeto que va a durar más que un otras referencias a ella. ¿Cree usted que el objeto posee en sí? ¿Y eso que significa? El objeto debe suprimirse cuando el objeto ya no existe? No tiene sentido.

Pero si no hay un solo dueño claramente definido, poner en práctica la propiedad compartida, por ejemplo utilizando un puntero inteligente aplicación de recuento de referencias, tales como boost::shared_ptr

Sin embargo, tener una función miembro en el objeto en sí, que está codificada para eliminar el objeto de un lista específica, si es o no existe allí, y si es o no también existe en cualquier otra lista y también eliminar el objeto en sí mismo, independientemente de la que existen referencias a ella, es una mala idea.

Otros consejos

Usted ha hecho Update una función virtual, lo que sugiere que las clases derivadas pueden reemplazar la implementación de Update. Esto introduce dos grandes riesgos.

1.) Una implementación reemplazado puede recordar hacer un World.Remove, pero puede olvidar el delete this. La memoria se filtró.

2.) La aplicación reemplazado llama al Update de la clase base, lo que hace un delete this, pero luego procede con más trabajo, pero con un inválido esta triple.

Considere este ejemplo:

class WizardsFire: public Fire
{
public:
    virtual void Update()
    {
        Fire::Update();  // May delete the this-object!
        RegenerateMana(this.ManaCost); // this is now invaild!  GPF!
    }
}

No hay nada realmente malo en sí mismos objetos de eliminar en C ++, la mayoría de las implementaciones de referencia que cuenta van a usar algo similar. Sin embargo, se miraba como una técnica un poco esotérica, y se necesita para mantener un ojo muy cerca de los problemas de propiedad.

El borrado se producirá un error y puede hacer que el programa si el objeto era no asignado y construido con el nuevo. Esta construcción le impedirá crear instancias de la clase en la pila o estáticamente. En su caso, usted parece estar utilizando algún tipo de esquema de asignación. Si nos atenemos a eso, este podría ser segura. Desde luego, no lo haría, sin embargo.

Yo prefiero un modelo de propiedad más estricta. El mundo debe poseer los objetos en él y será responsable de la limpieza para arriba. O bien tiene que quitar mundo hacer o tener actualización (u otra función) devolver un valor que indica que un objeto debe suprimirse.

También estoy adivinando que en este tipo de patrón, se le va a querer hacer referencia a contar sus objetos para evitar terminar con referencia colgante.

Ciertamente funciona para objetos creados por new y cuando la persona que llama de Update buena información acerca de ese comportamiento. Pero me gustaría evitarlo. En su caso, la propiedad se encuentra claramente en el mundo, por lo que haría que el mundo eliminarlo. El objeto no crea en sí, creo que también no debe borrarse a sí mismo. Puede ser muy sorprendente si se llama a una función "Actualizar" en el objeto, pero de repente ese objeto no está vigente más sin Mundial haciendo nada fuera de sí mismo (aparte de Extracción fuera de su lista - pero en otro marco el código de llamada actualización sobre el objeto no se dará cuenta de que).

Algunas ideas sobre este

  1. Añadir una lista de referencias a objetos a la Humanidad. Cada objeto en esa lista está pendiente para su eliminación. Esta es una técnica común, y se utiliza en wxWidgets para las ventanas de nivel superior que estaban cerradas, pero aún puede recibir mensajes. En el tiempo de inactividad, cuando se procesan todos los mensajes, la lista de pendientes se procesa y se eliminan los objetos. Creo Qt sigue una técnica similar.
  2. Envía el mundo quiere que el objeto que desea eliminar. El mundo debe estar correctamente informado y se hará cargo de cualquier materia que hay que hacer. Algo así como una deleteObject(Object&) tal vez.
  3. Añadir una función shouldBeDeleted a cada objeto que devuelve true si el objeto desea ser borrado por su propietario.

Yo preferiría la opción 3. El mundo llamaría actualización. Y después de eso, se ve si el objeto debe suprimirse, y puede hacerlo -. O si lo desea, se recuerda ese hecho mediante la adición de dicho objeto a una lista de espera de expulsión-manualmente

Es un dolor en el culo cuando no se puede estar seguro de cuándo y cuándo no ser capaz de acceder a las funciones y los datos del objeto. Por ejemplo, en wxWidgets, hay una clase wxThread que puede funcionar en dos modos. Uno de estos modos (llamado "desmontable") es que si sus principales declaraciones de función (y los recursos de rosca deben ser liberados), se borra a sí mismo (para liberar la memoria ocupada por el objeto wxThread) en lugar de esperar a que el dueño de la rosca oponerse a llamar a una función de espera o unirse. Sin embargo, esto causa dolor de cabeza severo. Nunca se puede llamar a cualquier función en ella, ya que podría haber sido terminado en cualquier circunstancia, y no se puede tener que no se creó con la nueva. Bastantes personas me dijeron que no les gusta mucho que el comportamiento de la misma.

El auto supresión de la referencia objeto contado está oliendo, en mi humilde opinión. Comparemos:

// bitmap owns the data. Bitmap keeps a pointer to BitmapData, which
// is shared by multiple Bitmap instances. 
class Bitmap {
    ~Bitmap() {
        if(bmp->dec_refcount() == 0) {
            // count drops to zero => remove 
            // ref-counted object. 
            delete bmp;
        }
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount();
    int inc_refcount();

};

Compare esto con objetos refcounted auto-eliminación de:

class Bitmap {
    ~Bitmap() {
        bmp->dec_refcount();
    }
    BitmapData *bmp;
};

class BitmapData {
    int dec_refcount() {
        int newCount = --count;
        if(newCount == 0) {
            delete this;
        }
        return newCount;
    }
    int inc_refcount();
};

Creo que la primera es mucho más agradable, y creo objetos contados de referencia bien diseñado hacer no do "Suprimir esta", ya que aumenta el acoplamiento: La clase utilizando la referencia contado de datos tiene que conocer y recordar acerca de que los datos borra a sí mismo como un efecto secundario de decrementar la referencia de recuento. Nótese cómo "bmp" se convierte posiblemente una referencia colgante en el destructor del ~ de mapa de bits. Podría decirse, sin hacer que "Suprimir esta" es mucho mejor aquí.

respuesta a una pregunta similar "¿Cuál es el uso de eliminar este "

Es una aplicación bastante común y recuento de referencias que he utilizado con éxito antes.

Sin embargo, también he visto que choque. Me gustaría poder recordar las circunstancias del accidente. @abelenky es un lugar que he visto estrellarse.

Podría haber sido donde más Fire subclase, pero no para crear un destructor virtual (véase más adelante). Cuando usted no tiene un destructor virtual, la función Update() llamará ~Fire() en lugar de la ~Flame() destructor apropiado.

class Fire : Object
{
public:
    virtual void Update()
    {
        if(age > burnTime)
        {
            world.Remove(this);
            delete this; //Make sure this is a virtual destructor.
        }
    }
};

class Flame: public Fire
{
private: 
    int somevariable;
};

Otros han mencionado problemas con "borrar esto." En resumen, ya que "mundo" gestiona la creación de fuego, sino que también debe gestionar su eliminación.

Otro problema es si el mundo se acaba con un fuego sigue ardiendo. Si este es el único mecanismo a través del cual un fuego puede ser destruido, entonces usted podría terminar con un huérfano o basura fuego.

Para la semántica que desea, yo tendría una bandera "activo" o "vivo" en su clase "Fuego" (u objeto en su caso). Entonces el mundo sería en ocasiones comprobar su inventario de objetos, y deshacerse de los que ya no están activos.

-

Una nota más es que su código como está escrito tiene fuego heredar privada del objeto, ya que es el valor por defecto, aunque es mucho menos común que inheiritance pública. Probablemente debería hacer el tipo de herencia explícita, y sospecho que realmente quiere inheritiance público.

No es generalmente una buena idea para hacer un "eliminar este" salvo que sea necesario o se utiliza de una manera muy sencilla. En su caso parece que el mundo solo puede eliminar el objeto cuando se retira, a menos que existan otras dependencias que no estamos viendo (en cuyo caso la llamada de eliminación provocará errores, ya que no son notificados). Mantener la propiedad fuera de su objeto le permite a su objeto a ser mejor y más estable encapsulado:. El objeto no dejará de ser válida en algún momento simplemente porque eligió a borrarse a sí mismo

No lo hago no hay nada inherentemente malo en un objeto de eliminar en sí, pero otro método posible sería tener el mundo sea responsable de la eliminación de objetos como parte de :: Eliminar (suponiendo que también se eliminan todos los objetos retirados.)

Suprimir esta es muy arriesgado. No hay nada "malo" con él. Es legal. Pero hay tantas cosas que pueden salir mal cuando lo utiliza que debe utilizarse con moderación.

Considere el caso de que alguien tiene punteros abiertos o las referencias al objeto, y luego se va y borra a sí mismo.

En la mayoría de los casos, creo que la duración del objeto debe ser gestionado por el mismo objeto que la crea. Sólo hace que sea más fácil saber dónde se produce la destrucción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top