Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Destruction d'objets dans une classe template


Sujet :

Langage C++

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    ...
    Inscrit en
    juin 2009
    Messages
    4 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : ...

    Informations forums :
    Inscription : juin 2009
    Messages : 4 306
    Points : 12 938
    Points
    12 938
    Billets dans le blog
    1
    Par défaut Destruction d'objets dans une classe template
    Bonjour,

    Dans mon code, j'ai un pool d'objets alloués statiquement. J'ai une fonction emplace() pour réserver un slot et faire un placement new sur un élément du pool. J'ai une fonction release() qui fait un appel au destructeur d'un élément et relâcher le slot.

    Pour simplifier, j'ai fait ce code qui est en fait un pool avec un seul élément mais qui permet de bien représenter mon contexte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    #include <iostream>
     
    template<typename T>
    class Pool {
    public:
    	void release() {
    		if (used_m) {
    			t_m.~T();
    			used_m = false;
    		}
    	}
     
    	template<typename ... Args>
    	void emplace(Args&& ... args) {
    		if (not used_m) {
    			new(&t_m) T{std::forward<Args>(args)...};
    			used_m = true;
    		}
    	}
     
    private:
    	bool used_m = false;
    	T t_m{};
    };
     
    class A {
    public:
    	A() {
    		std::cout << __PRETTY_FUNCTION__ << '\n';
    	}
     
    	A(int i, float f) {
    		std::cout << __PRETTY_FUNCTION__ << ' ' << i << ' ' << f << '\n';
    	}
     
    	~A() {
    		std::cout << __PRETTY_FUNCTION__ << '\n';
    	}
    };
     
    int main() {
    	Pool<A> pa;
    	pa.emplace(42, 3.14f);
    	pa.release();
     
    	Pool<int> pi;
    	pi.emplace(42);
    	pi.release();
    }
    Sortie console lors de l'exécution :
    $ untitled.exe
    A::A()
    A::A(int, float) 42 3.14
    A::~A()
    A::~A()
    
    Process finished with exit code 0
    Jusque là, tout me semble OK. Cette discussion stackoverflow me fait comprendre que mon code est valide : https://stackoverflow.com/questions/...s-int-char-etc

    En revanche, si je change mon main() :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main() {
    	Pool<int[16]> pia;
    	pia.emplace(42, 66, 99);
    	pia.release(); // ceci est à la ligne 44
    }
    alors mon code ne compile plus :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    [ 50%] Building CXX object CMakeFiles/untitled.dir/main.cpp.obj
    main.cpp: In instantiation of 'void Pool<T>::release() [with T = int [16]]':
    main.cpp:44:14:   required from here
    main.cpp:8:9: error: request for member '~int [16]' in '((Pool<int [16]>*)this)->Pool<int [16]>::t_m', which is of non-class type 'int [16]'
        t_m.~T();
        ~~~~~^
    CLion me met aussi une indication d'erreur sur cette ligne :
    Nom : pseudo destructor.png
Affichages : 57
Taille : 5,8 Ko

    Une recherche me confirme que le problème vient bien du fait que T est un tableau et non un scalar type.

    Je me pose des questions sur la meilleure solution pour éviter cette erreur de compilation.

    J'ai d'abord pensé à quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void release() {
    	if (used_m) {
    		if constexpr (std::is_destructible_v<T>) {
    			t_m.~T();
    		}
    		used_m = false;
    	}
    }
    Mais cela ne fonctionne pas car le code est toujours compilé.

    Vous avez des idées à me proposer ? Merci d'avance !

  2. #2
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    ...
    Inscrit en
    juin 2009
    Messages
    4 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : ...

    Informations forums :
    Inscription : juin 2009
    Messages : 4 306
    Points : 12 938
    Points
    12 938
    Billets dans le blog
    1
    Par défaut
    En creusant un peu plus le lien vers stackoverflow, je me rend que la solution est applicable dans mon cas :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    template<typename T>
    class Pool {
    public:
    	void release() {
    		if (used_m) {
    			destroy(t_m);
    			used_m = false;
    		}
    	}
     
    	template<typename ... Args>
    	void emplace(Args&& ... args) {
    		if (not used_m) {
    			new(&t_m) T{std::forward<Args>(args)...};
    			used_m = true;
    		}
    	}
     
    private:
    	bool used_m = false;
    	T t_m{};
     
    	template<typename U>
    	void destroy(U& u) {
    		std::cout << __PRETTY_FUNCTION__ << '\n';
    		u.~U();
    	}
     
    	template<typename U, std::size_t N>
    	void destroy(U (&)[N]) {
    		std::cout << __PRETTY_FUNCTION__ << '\n';
    	}
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int main() {
    	Pool<int[16]> pia;
    	pia.emplace(42, 66, 99);
    	pia.release();
     
    	Pool<A> pa;
    	pa.emplace(42, 3.14f);
    	pa.release();
     
    	Pool<int> pi;
    	pi.emplace(42);
    }
    $ untitled.exe
    void Pool<T>::destroy(U (&)[N]) [with U = int; long long unsigned int N = 16; T = int [16]]
    A::A()
    A::A(int, float) 42 3.14
    void Pool<T>::destroy(U&) [with U = A; T = A]
    A::~A()
    void Pool<T>::destroy(U&) [with U = int; T = int]
    A::~A()
    
    Process finished with exit code 0
    Il y a mieux à faire ?

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    janvier 2020
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : janvier 2020
    Messages : 6
    Points : 26
    Points
    26
    Par défaut
    Je ne vois pas mieux. Par contre, tu n'appelles pas les destructeurs des objets de ton tableau dans ton void destroy(U (&)[N]) (comme montré dans la réponse sur Stack Overflow).

  4. #4
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2011
    Messages
    628
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2011
    Messages : 628
    Points : 3 093
    Points
    3 093
    Par défaut
    std::destroy_at.

    D'ailleurs, les codes sur cppreference et stackoverflow font tous deux quelque chose que ta fonction destroy ne fait pas: appliquer la destruction sur chaque élément du tableau (qui peuvent eux-mêmes être des tableaux).

  5. #5
    Expert confirmé
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    avril 2016
    Messages
    1 052
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : avril 2016
    Messages : 1 052
    Points : 4 492
    Points
    4 492
    Par défaut
    Bonjour,

    Attention, il y a aussi une erreur ici :
    Citation Envoyé par Bktero Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    private:
    	bool used_m = false;
    	T t_m{};
    };
    Le constructeur par défaut de Pool<T> construit un objet de type T dans la variable t_m, mais initialise used_m à false, ce qui est incohérent. Tu vas avoir des fuites de mémoire pour les types T dont le constructeur par défaut alloue de la mémoire.
    En plus, ton implémentation ne peut compiler qu'avec les types T qui ont un constructeur par défaut. Il vaut mieux avoir une variable membre de type std::aligned_storage_t<sizeof(T), alignof(T)>, comme dans ton lien StackOverflow. Voir la doc de std::aligned_storage.

    À part ça, ton template Pool est une variante de std::optional.

    De plus, pour éviter certaines irrégularités du C++ autour des tableaux natifs, je conseille plutôt std::array.

    Si c'est possible, je conseille de remplacer Pool<int[16]> par std::optional<std::array<int, 16>>.
    Remarque : de toute façon, std::optional n'est pas compatible avec les tableaux natifs.

Discussions similaires

  1. typedef dans une class template
    Par yan dans le forum Langage
    Réponses: 2
    Dernier message: 01/10/2007, 11h43
  2. list d'objet dans une classe
    Par wadcyr8_197 dans le forum C++
    Réponses: 10
    Dernier message: 04/07/2007, 16h34
  3. Réponses: 8
    Dernier message: 12/04/2007, 12h32
  4. Class interne dans une classe template
    Par MatRem dans le forum Langage
    Réponses: 26
    Dernier message: 15/06/2006, 11h45
  5. [POO] import d'objet dans une classe
    Par wdionysos dans le forum Langage
    Réponses: 3
    Dernier message: 01/04/2006, 22h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo