IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

C++ Discussion :

push_back une anonymous struct dans un vector


Sujet :

C++

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut push_back une anonymous struct dans un vector
    Bonjour,

    Je trouve le code suivant pas terrible :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct Base {};
     
    std::vector<Base*> vector;
     
    struct : Base {} derivedInstance; // struct sans type, mais avec un nom
     
    vector.push_back(&derivedInstance);
    J'aimerais rendre la chose un peu plus "générique", sans avoir à définir un nom à chaque fois que je veux ajouter une structure fille dans mon vector. Y aurait-il un moyen d'écrire quelque chose dans ce style là ? :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Base {};
     
    std::vector<Base*> vector;
     
    vector.push_back(new struct : Base {}); // struct sans type, et sans nom
    Merci

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 633
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Salut,
    Citation Envoyé par MrPchoun Voir le message
    Bonjour,

    Je trouve le code suivant pas terrible :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct Base {};
     
    std::vector<Base*> vector;
     
    struct : Base {} derivedInstance; // struct sans type, mais avec un nom
     
    vector.push_back(&derivedInstance);
    J'aimerais rendre la chose un peu plus "générique", sans avoir à définir un nom à chaque fois que je veux ajouter une structure fille dans mon vector. Y aurait-il un moyen d'écrire quelque chose dans ce style là ? :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Base {};
     
    std::vector<Base*> vector;
     
    vector.push_back(new struct : Base {}); // struct sans type, et sans nom
    Merci
    Heu, excuses moi, mais, d'où sors tu cette syntaxe infame
    Normalement, si tu as une hiérarchie de classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Base{
        /* ... */
    };
    class Derivee : public Base {
        /*...*/
    }
    tu peux envisager d'utiliser un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(){
        Base b;
        Derivee d;
        std::vector<Base *> tab;
        tab.push_back(&b);
        tab.push_back(&d);
        return 0;
    }
    MAIScela n'ira pas sans poser un certain nombre de problème, car les pointeurs que l'on trouvera dans tab ne seront valides que si b et d existent encore. Ainsi, si on fait en sorte que la portée de tab soit "un tout petit peu plus grande" que celle des variables, en modifiant main pour qu'il ressemble à quelque chose comme
    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
    int main(){
        std::vector<Base *> tab;
        if(conditon){
            Base b;
            tab.push_back(&b); 
        }else{    //CRACK b es détruit ici... le pointeur équivalant dans tab est invalidé
            Derivee d;
            tab.push_back(&d);
       }//CRACK d es détruit ici... le pointeur équivalant dans tab est invalidé
        //tab existe toujours ici, mais les pointeurs qu'il contient sont tous invalides
        for(size_t i = 0;i<tab.size();++i){
            tab[i]->doSomething(); // BOUM :déréférencement d'une adresse invalide
                                   // ==>comportement indéfini (sans doute une erreur de segmentation
        }
        return 0;
    }
    Du coup, il faut s'assurer que la durée de vie des objets dont tu prend l'adresse pour les pointeurs dépasse la portée dans laquelle ils sont créés, ce qui t'oblige à passer par l'allocation dynamique de la mémoire. un code proche de
    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
    int main(){
        std::vector<Base *> tab;
        if(conditon){
            Base  *b = new Base;
            tab.push_back(b);
            /*    OU    OU    OU 
            tab.push_back(new Base);
            */ 
        }else{   
            Derivee * d = new Derivee;
            tab.push_back(d);
            /*    OU    OU    OU
            Base * d = new Derivee;
            tab.push_back(&d);
                  OU    OU    OU
            tab.push_back(new Derivee);
       }
        for(size_t i = 0;i<tab.size();++i){
            tab[i]->doSomething(); 
        }
        /* N'oublions pas de libérer la mémoire allouée pour les pointeurs */
     
        for(size_t i = 0;i<tab.size();++i){
            delete tab[i];
        }
        return 0;
    }
    Seulement, le choix du moment où il faut veiller à libérer la mémoire allouée à un pointeur est très rarement aussi simple que cela... De plus, C++ est un langage à exceptions et il se fait que les exceptions brisent totalement l'ordre d'exécution. Il est donc très facile d'en arriver à des situations où le fait qu'une exception a été lancée risque d'occasionner des fuites mémoires.

    C'est la raison pour laquelle la norme C++11 (l'avant dernière en date) est arrivée avec la notion de "pointeurs intelligents". Ce sont des classes qui ont le bon gout de prendre en charge la décision de libérer correctement la mémoire allouée à un pointeur juste avant que l'on ne perte tout trace de l'adresse où cette mémoire se trouve. Je ne pourrais donc que te conseiller particulièrement vivement de t'y intéresser très rapidement
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    750
    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 : 750
    Par défaut
    Quand je vois un usage abusif de structure anonyme, je me dis qu'on ne veut pas des classes, mais des fonctions polymorphiques.

    @MrPchoun: Combien y a-t-il de fonctions virtuelles dans la classe de base ? Une ? Si oui, regarde std::function + lambda.

  4. #4
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    @koala01 : par rapport aux deux premiers codes que tu as pondu : j'ai pas besoin de donner un nom à la structure, parce qu'il n'y a qu'une seule instance de cette structure dans mon vector, et dans tout mon programme d'ailleurs. En fait, le vector est rempli de structures uniques, qui dérivent toutes de Base. Une autre fonction se charge de créer le contenu des classes dérivées (membres et fonctions spécifiques).
    Et je comprends tout à fait qu'il vaut mieux faire de l'allocation dynamique de mémoire et pas du référencement, pour les problèmes que tu as pointé (oh oh brillant jeu de mot), c'est d'ailleurs en fait l'objet de mon post, puisque cette solution ne me convenait pas (cf mon premier post). Et je connais bien évidement les smarts pointer, mais ils ne me sont d'aucune utilité, parce que j'ajoute toutes mes structures dérivées dans mon vector au début du programme, et qu'ensuite ça ne bouge plus : aucun risque de suppression.

    @jo_link_noir : alors j'ai fini par faire ça, mais pas dans le sens où tu l'entends je crois ! J'ai même fini par me passer des pointeurs d'ailleurs, par un petit tour de passe passe qui me surprend encore

    Je suis passé par ça :

    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
     
        struct Base
        {
            std::function<void()> doSomething;
        };
     
        std::vector<Base> vector;
     
        struct Derived : Base
        {
            int a = 10;
     
            Derived()
            {
                doSomething = [this]
                {
                    std::cout<<a;
                };
            }
        };
     
        vector.push_back(Derived());
     
        vector[0].doSomething();
    Pour le transformer en ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
        struct Base
        {
            std::function<void()> doSomething;
        };
     
        std::vector<Base> vector;
     
        vector.push_back([](){ struct Derived : Base { int a = 10; Derived() { doSomething = [this] { std::cout<<a; }; } }; return Derived(); }());
     
        vector[0].doSomething(); // affiche 10
    Je suis donc passé par une fonction lambda qui s'appelle elle même, qui crée la structure dérivée et qui en retourne une instance, celle qu'on mettra dans mon vector. Le nom de ma structure dérivée est local, dans la lambda, ça me va très bien, j'ai pas à l'utiliser autre part.

    Ce qui me surprend, c'est qu'on a un vector de Base, avec des Derived dedans, et pourtant les Derived ont accès à leur membres, membres que n'ont pas Base (a). Ça me semble bizare. Mais ça me convient.

  5. #5
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    ils ont accès à leur membre tant qu'ils n'ont pas été mis dans le vector: le temps de l'exécution de la lambda.
    Après, chaque Derived est utilisé pour initialiser (par copie) un Base.

    En fait, si tu essayais d'utiliser vector.emplace_back(), (sans passer par le constructeur de copie...) ca ne marcherait plus vraiment

  6. #6
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    Mais le code que je viens d'écrire est "valide" ? Je veux dire, il est portable, avec tous les compilateurs, ou c'est juste clang qui m'accorde une faveur ?

  7. #7
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Il est peut-être valide, mais je serais personnellement très réticent vis à vis d’un tel code. De manière générale, je n’aime pas tout ce qui est anonyme, à l’exception des namespaces.

    C’est quoi le but ?

  8. #8
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    Bah c'est même plus anonyme du coup, juste limité au lambda..

    Le but : je me triture l'esprit pour faire un Entity Component System propre. Base, c'est un System. Derived, c'est un System qui traitera certaines Entity, en fonction de leurs Components. Et doSomething, c'est la fonction d'update. Voilou !

  9. #9
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    750
    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 : 750
    Par défaut
    Pas valide, la lambda fait référence à this qui n'est plus de type Derived. Par contre vector.push_back({[](){ struct Derived { A a = 10; void operator()(){ std::cout<<a; } }; return Derived(); }()}); fonctionne car doSomething est initialisé avec le foncteur (qui n'est plus un type dérivé).

    Mais vector.push_back({[a=10] { std::cout<<a; } }); est beaucoup plus simple et lisible. (c++14)
    (Avec std::vector<std::function<void()>> vector;)

  10. #10
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Citation Envoyé par MrPchoun Voir le message
    Ce qui me surprend, c'est qu'on a un vector de Base, avec des Derived dedans, et pourtant les Derived ont accès à leur membres, membres que n'ont pas Base (a). Ça me semble bizare. Mais ça me convient.
    Ou pour le dire autrement, une fois dans le vector, ce ne sont plus des Derived mais uniquement des Base. Ca compile mais n'aura pas le comportement que tu attends, la nature spécialisée des Derived et les données qui vont avec sont perdus. Pour que cela fonctionne, il vaut mieux utiliser un vecteur d'unique_ptr (si tu veux que le vecteur soit propriétaire des objets, ce qui est le cas dans tes exemples) ou un vecteur de reference_wrapper ou de pointeurs.

  11. #11
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Dans tous les cas, ça me parait un peu dégueulasse.

    Comme je vois « Entity Component System propre », je m’inquiète un peu sur la pertinence de la piste. Après, si c’est un détail d’implémentation très localisé, ça peut se comprendre, mais si tout le système repose sur ce genre de truc, et que c’est exposé à l’utilisateur final, alors il y a vraisemblablement un problème de conception.

  12. #12
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    @jo_link_noir : oui, mais il n'y a pas qu'une seule fonction virtuelle dans Base !

    Dans tous les cas, ça me parait un peu dégueulasse.
    J'apprécie ton honnêteté, c'est à la fois rigolo et sévère. Formulé comme ça, ça me va ^^

    Je propose quelque chose de moins dégueulasse, du moins je crois, et je me résous au polymorphisme/pointeurs, pour éviter les problèmes que soulignent jblecanard :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Base {};
     
    std::vector<Base*> vector;
     
    [&](){ struct Derived : Base {int a; void doSomething() {std::cout<<a;} }; vector.push_back(new Derived); }();

  13. #13
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par MrPchoun Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Base {};
     
    std::vector<Base*> vector;
     
    [&](){ struct Derived : Base {int a; void doSomething() {std::cout<<a;} }; vector.push_back(new Derived); }();
    Deux remarques :
    * Base devrait avoir un destructeur virtuel
    * dans ton vector, tu devrais stocker des unique_ptr et pas des pointeurs nus

    Pour le reste, je n’ai pas une vision suffisante du contexte .

  14. #14
    Membre confirmé
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Par défaut
    Vendu !

    Merci pour vos remarques

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 24/07/2009, 23h12
  2. Utiliser une classe personnelle dans un vector
    Par Mindiell dans le forum SL & STL
    Réponses: 16
    Dernier message: 01/03/2007, 17h42
  3. passer unee struct dans un buffer (char *)
    Par baert dans le forum C++
    Réponses: 2
    Dernier message: 20/02/2006, 21h49
  4. mettre un struct dans un vector
    Par Biosox dans le forum SL & STL
    Réponses: 2
    Dernier message: 02/02/2006, 16h34
  5. implementer une struct dans un .c
    Par jamal dans le forum C
    Réponses: 10
    Dernier message: 10/03/2005, 19h52

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