free(): puntero inválido cuando se utiliza get() en lugar de release() en unique_ptr
Tengo el siguiente programa:
#include <list>
#include <stack>
#include <memory>
struct ChildNode {};
struct value_node_ptr : public std::unique_ptr<childnode> {
using std::unique_ptr<childnode>::unique_ptr;
using std::unique_ptr<childnode>::reset;
};
struct Node {
std::list<value_node_ptr> children;
void addChild(ChildNode* child){
children.emplace_back(child);
}
};
void iterateChildren(Node* node){
for(auto it=node->children.begin(); it != node->children.end(); ++it){
auto child = it->release();
//do stuff with child
}
}
int main() {
ChildNode child;
Node node;
node.addChild(&child);
std::stack<node*> nodes;
nodes.push(&node);
iterateChildren(nodes.top());
nodes.pop();
}
Esto funciona bien. Pero si uso it->get()
en lugar de it->release()
en la función iterateChildren
, obtengo el error:
free(): invalid pointer
Quiero reutilizar los hijos en algún momento y pensé que it->release()
destruiría el puntero para que no sea utilizable después, pero parece que el puntero sigue ahí, lo cual me confunde.
Además, pensé que it->get()
era la opción correcta ya que solo quiero obtener el valor y no destruir el puntero. Estaba pensando que podría haber algún error de fuga de memoria al llamar a it->get()
, por lo que intenté llamar a it->reset()
en cada uno de los hijos después de haber terminado de usarlos, pero esto tampoco resolvió el error.
¿Alguien puede explicarme este comportamiento y qué método debo usar?</node*>
davy.ai
La razón por la cual el uso de
it->get()
causa el error es que devuelve un puntero sin procesar al objeto propiedad del unique_ptr. El unique_ptr sigue siendo el propietario del objeto y lo eliminará cuando quede fuera del ámbito. Sin embargo, estás accediendo al objeto a través de un puntero sin procesar después de que ya haya sido eliminado, lo que resulta en un comportamiento indefinido y, en este caso, un error “free(): invalid pointer”.Por otro lado,
it->release()
transfiere la propiedad del objeto a ti, dejando el unique_ptr en un estado nulo. Esto significa que tú asumes la responsabilidad de eliminar el objeto y puedes accederlo de forma segura a través del puntero sin procesar. Por eso, el código funciona cuando se utilizait->release()
en lugar deit->get()
.Si deseas reutilizar los hijos más tarde, puedes retener los punteros sin procesar y crear un mecanismo para eliminarlos cuando hayas terminado con ellos. Alternativamente, puedes envolver los punteros sin procesar en otro puntero inteligente como shared_ptr o weak_ptr, que se encargarán de la propiedad de los objetos.
Usar
it->reset()
después de usar el puntero sin procesar obtenido deit->get()
no resolverá el error porque reset() también elimina el objeto propiedad del unique_ptr, dejándote con un puntero nulo. Esto significa que tendrá el mismo efecto que usarit->release()
pero sin transferir la propiedad.Por lo tanto, en este caso, es apropiado utilizar
it->release()
para transferir la propiedad del objeto a ti, de manera que puedas acceder al objeto de forma segura sin obtener ningún error.