Dans quel cas faut-il implémenter un deleter pour un unique_ptr ? Notamment si je dois nettoyer la structure pointée avant qu'elle soit libérée ? Le deleter par défaut est appelé par le destructeur de MaClasse ?
Le deleter par défaut est appelé par le destructeur de unique_ptr après que le destructeur de ta classe a fini de s'exécuter (cf ma première réponse). En l’occurrence, tu pourrais le remplacer par un Deleter qui appelle la fonction de nettoyage C avant de libérer la mémoire :
#include<memory>#include<iostream>#include<exception>extern"C"{structmy_c_struct{intdata;};ints_init(my_c_struct*c){std::cout<<"C initializing\n";return1;}voids_destroy(my_c_struct*c){std::cout<<"C clean up\n";}}classMyClass{public:MyClass();private:structDeleter{voidoperator()(my_c_struct*p){s_destroy(p);deletep;}};std::unique_ptr<my_c_struct,Deleter>s;};MyClass::MyClass():s(newmy_c_struct){intrc=s_init(s.get());if(rc!=0){throwstd::runtime_error("could not initialize c_struct, rc = "+std::to_string(rc));}}intmain(){try{MyClassm;// allocation s'est bien passée, je peux continuer ?}catch(std::exceptionconst&e){std::cout<<"runtime error: "<<e.what()<<'\n';return42;}return0;}
C'est une approche un peu bâtarde à mon goût parce que les responsabilités d'initialisation et de de nettoyage C ne sont pas du ressort du même objet : l'initialisation est faite dans le constructeur de MyClass et le nettoyage dans le Deleter du unique_ptr. Du point de vue ingénierie, ce n'est pas particulièrement idéal, d'ailleurs tu noteras que le code de nettoyage C est lancé même si l'initialisation échoue. Ça dépend probablement de ton API C, mais en général, c'est inutile, voire dangereux. Pour le coup, il serait peut-être préférable de laisser la responsabilité de gérer la mémoire au unique_ptr et de laisser à ta classe de gérer l'initialisation / nettoyage :
#include<memory>#include<iostream>#include<exception>extern"C"{structmy_c_struct{intdata;};ints_init(my_c_struct*c){std::cout<<"C initializing\n";return1;}voids_destroy(my_c_struct*c){std::cout<<"C clean up\n";}}classMyClass{public:MyClass();~MyClass();private:std::unique_ptr<my_c_struct>s;};MyClass::MyClass():s(newmy_c_struct){intrc=s_init(s.get());if(rc!=0){throwstd::runtime_error("could not initialize c_struct, rc = "+std::to_string(rc));}}MyClass::~MyClass(){s_destroy(s.get());}intmain(){try{MyClassm;// allocation s'est bien passée, je peux continuer ?}catch(std::exceptionconst&e){std::cout<<"runtime error: "<<e.what()<<'\n';return42;}return0;}
Ici, le code de nettoyage C n'est appelé que si l'initialisation C a abouti.
/ et je gère le fait que l'allocation s'est mal passée ?
Le catch va récupérer toutes les erreurs, notamment les erreurs d'allocation, et l'exception due à une mauvaise initialisation en C. Mais au moins toutes les ressources auront déjà été libérées au moment du catch, en gros, à ce niveau tu n'as plus qu'à logguer, tout l reste est déjà « géré ». Le fait de confier la libération des ressources aux destructeurs des objets te permet d'avoir une logique try - catch relativement simple comparé à d'autres langages.
[^] # Re: quelques éléments de réponse
Posté par nazcafan . En réponse au message classe C++, membre structure C et allocation dynamique. Évalué à 5.
Le deleter par défaut est appelé par le destructeur de
unique_ptraprès que le destructeur de ta classe a fini de s'exécuter (cf ma première réponse). En l’occurrence, tu pourrais le remplacer par un Deleter qui appelle la fonction de nettoyage C avant de libérer la mémoire :C'est une approche un peu bâtarde à mon goût parce que les responsabilités d'initialisation et de de nettoyage C ne sont pas du ressort du même objet : l'initialisation est faite dans le constructeur de MyClass et le nettoyage dans le Deleter du
unique_ptr. Du point de vue ingénierie, ce n'est pas particulièrement idéal, d'ailleurs tu noteras que le code de nettoyage C est lancé même si l'initialisation échoue. Ça dépend probablement de ton API C, mais en général, c'est inutile, voire dangereux. Pour le coup, il serait peut-être préférable de laisser la responsabilité de gérer la mémoire auunique_ptret de laisser à ta classe de gérer l'initialisation / nettoyage :Ici, le code de nettoyage C n'est appelé que si l'initialisation C a abouti.
Le
catchva récupérer toutes les erreurs, notamment les erreurs d'allocation, et l'exception due à une mauvaise initialisation en C. Mais au moins toutes les ressources auront déjà été libérées au moment ducatch, en gros, à ce niveau tu n'as plus qu'à logguer, tout l reste est déjà « géré ». Le fait de confier la libération des ressources aux destructeurs des objets te permet d'avoir une logiquetry-catchrelativement simple comparé à d'autres langages.