Pregunta

A menudo agrego un método Empty a mis objetos C ++ para borrar el estado interno usando un código similar al siguiente.

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

Esto parece ser mejor que duplicar código en el constructor, pero me preguntaba si * this = Foo () es un enfoque común cuando se desea borrar un objeto. ¿Hay algún problema con esto esperando para morderme en la parte trasera? ¿Hay alguna otra forma mejor de lograr este tipo de cosas?

¿Fue útil?

Solución

Dejaría que el constructor llamara a mi función en su lugar:

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

Sí, primero está inicializando innecesariamente la cadena como una cadena vacía, luego haciendo una tarea, pero esto es mucho más claro.

Otros consejos

¿Problemas potenciales? ¿Cómo sabes que * esto realmente es un Foo?

Lo que está haciendo con este método Empty es esencialmente lo mismo que asignar manualmente un objeto recién construido a una variable (lo que hace la función Empty).

Personalmente, eliminaría el método Empty y reemplazaría todos los usos de ese método con esto:

// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;

// replace line "foo.Empty()" with:
foo = Foo();

// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();

Realmente no veo ninguna ventaja en tener este método Empty. Oculta lo que realmente le está sucediendo al objeto en la bruja que se llama, el nombre tampoco es la mejor opción.

Si la persona que llama realmente quiere un objeto limpio, no tendrá problemas para construir el objeto él mismo.

Además, considere hacer que los objetos sean inmutables, es decir, , cuando se construyen, no se pueden cambiar. Esto puede, en muchos escenarios, salvarse de efectos secundarios imprevistos.

Hay algo aún más común de lo que has sugerido. usando el intercambio.

Básicamente haces algo como esto:

T().swap(*this);

dado que muchos contenedores estándar (¿todos los contenedores STL?) tienen un método de intercambio de tiempo constante, esta es una manera simple y agradable de limpiar un contenedor y asegurarse de que se libere el almacenamiento.

Del mismo modo, es una buena manera de "encogerse para encajar" un contenedor también, pero usando el constructor de copia en lugar del constructor predeterminado.

considere usar la ubicación new :

void Empty() {
    this->~Foo();
    new (this) Foo();
}

Su código invoca operator = , lo que puede provocar todo tipo de efectos secundarios.

EDITAR en respuesta a los comentarios. - Este código está definitivamente bien definido, el estándar lo permite explícitamente. Publicaré el párrafo más tarde si encuentro el tiempo. Acerca de delete , por supuesto. Lo que quise decir fue ~ Foo () , esto fue un descuido. Y sí, Rob también tiene razón; Destruir el objeto es realmente necesario aquí para llamar al destructor de la cadena.

Esto puede ser una fuente potencial de pérdida de memoria si tiene una memoria asignada dinámicamente en el constructor.

Así es como lo hago:

class Foo {
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        Foo f;
        swap(f);
    }

    void swap(Foo & other) {
        std::swap(n_, other.n_);
        swap(str_, other.str_);
    }
};

void swap(Foo & one, Foo & other) {
    one.swap(other);
}

Coloque la función de intercambio en el mismo espacio de nombres como la clase Foo. La búsqueda dependiente de argumentos lo encuentra cuando los usuarios hacen una llamada para intercambiar para intercambiar dos Foo. Puede implementar operator = utilizando su función de intercambio también.

  

Esto parece ser mejor que duplicar código en el constructor, pero me preguntaba si * this = Foo () es un enfoque común cuando se desea borrar un objeto?

Borrar un objeto no es algo tan común: más comúnmente, un objeto (tal vez incluso un objeto inmutable) se instancia y contiene datos reales, o no se instancia.

El tipo de cosa más común que se restablece serían los contenedores ... pero, no estaría escribiendo sus propias clases de contenedores ahora, ¿verdad?

  

¿Hay algún problema con esto esperando para morderme en la parte trasera?

Sí:

  • Si esto no es realmente un Foo sino un DerivedFoo
  • Si el operador de asignación de Foo no existe, o si tiene errores (por ejemplo, si no está definido y el operador predeterminado no es bueno, por ejemplo, debido a que un miembro de datos es un puntero desnudo ).
  

¿Hay alguna otra forma mejor de lograr este tipo de cosas?

Sí, quizás una función libre sería mejor (evitaría los dos problemas anteriores):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}

Sí, esto no es eficiente en términos de rendimiento (creando otro objeto foo en lugar de trabajar en su lugar) y le morderá si asigna memoria en el constructor con una pérdida de memoria desagradable.

Hacerlo más seguro en términos de memoria sería llamar this- > delete and this = new foo () - pero será LENTO.

si quieres ser superrápido, crea un campo de objeto vacío estático y escríbelo en Restablecer.

si quiere ser rápido, asigne las propiedades una por una.

si desea mantener un estilo razonable sin duplicación, llame a Restablecer desde Ctor como lo sugirió Ates Goral, pero perderá la construcción más rápida con los parámetros predeterminados.

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