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

Langage C++ Discussion :

Transfert de unique_ptr d'un vector à un autre


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2017
    Messages
    93
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mars 2017
    Messages : 93
    Par défaut Transfert de unique_ptr d'un vector à un autre
    Bonjour,

    Voici un exemple dans lequel j'ai trois classes : Card, Player et Game. La classe Player contient un vector de unique_ptr de Card et la classe Game aussi.

    La classe Player dispose d'une méthode addCard qui permet d'ajouter une carte dans la main du joueur.
    La classe Game dispose aussi d'une méthode drawCard qui retourne la première carte du tas après l'avoir retirée.

    On a donc le code suivant :

    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
    #include <vector>
    #include <memory>
     
    class Card{};
     
    class Player{
      public:
        std::vector<std::unique_ptr<Card>> hand;
     
        void addCard(std::unique_ptr<Card> card);
    };
     
    void Player::addCard(std::unique_ptr<Card> card){
      hand.push_back(card);
    }
     
    class Game{
      public:
        std::vector<std::unique_ptr<Card>> deck;
     
        std::unique_ptr<Card> drawCard();
    };
     
    std::unique_ptr<Card> Game::drawCard(){
      std::unique_ptr<Card> card = std::move(deck.front());
      deck.erase(deck.begin());
      return card;
    }
    Idéalement, j'aimerais bien ajouter directement une carte dans la main du joueur en la piochant dans le tas du jeu :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Player player;
    // Game game;
     
    player.addCard(game.drawCard());
    Seulement ce code ne compile pas (notamment avec la fonction addCard), j'ai plusieurs questions :

    - le code de ma fonction drawCard() est-il correct? Le but est de retirer le premier élément du vector et de le retourner par valeur

    - Comment transférer un unique_ptr d'un vector à un autre?

    Merci d'avance, si ce n'est pas clair n'hésitez pas à me poser des questions

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    std::move
    Maintenant, d'un point de vue architecture, est-ce que ça a du sens que le joueur prenne possession de la carte ? Imo la carte devrait appartenir au jeu, et le joueur n'en a qu'un pointeur.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2017
    Messages
    93
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mars 2017
    Messages : 93
    Par défaut
    Bonjour,

    Merci pour votre réponse.

    Ok au niveau de l'architecture ça n'a peut être pas beaucoup de sens mais je veux représenter le fait que le joueur pioche une carte depuis la pioche. La carte piochée n'est donc plus dans la pioche mais dans la main du joueur.

    Dans ce cas comment est-ce que je peux le modéliser?

    Le fait que j'utilise des smart pointer (ici unique ptr) est motivé par le fait de ne pas avoir à s'occuper des opérations mémoire mais aussi cela veut dire que le pointeur sur une carte est unique. Si la carte est unique, alors je ne vois pas comment représenter le fait que le joueur en possède une dans sa main. D'accord elle appartient au jeu mais maintenant elle est contenue dans la main du joueur.

    Je tiens aussi à préciser que j'utilise de pointeurs dans cet exemple car j'ai besoin de polymorphisme dans mon application.

    J'ai un peu de mal à représenter ça en C++ contrairement à d'autres langages comme Ruby ou Java

    Merci encore de prendre le temps!

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    Pour transférer d'une collection vers une variable, tu écris correctement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::unique_ptr<Card> card = std::move(deck.front());
    Par contre quand tu écris hand.push_back(card); tu ne transfères pas une variable dans une collection, tu tentes de copier. Ton compilateur doit te le dire. Il oublie de préciser qu'il manque un std::move.

  5. #5
    Membre éclairé
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    70
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Février 2013
    Messages : 70
    Par défaut
    Citation Envoyé par dalfab Voir le message
    Bonjour,

    Pour transférer d'une collection vers une variable, tu écris correctement :
    Par contre quand tu écris hand.push_back(card); tu ne transfères pas une variable dans une collection, tu tentes de copier. Ton compilateur doit te le dire. Il oublie de préciser qu'il manque un std::move.
    Effectivement, l'opérateur = de std::unique_ptr n'accepte que des rvalues. Les versions qui opéreraient sur les lvalues ont été intentionnellement supprimées avec =delete.

    Depuis C++ 2011, toutes ces formes de l'opérateur = sont permises https://en.cppreference.com/w/cpp/la...ove_assignment et https://en.cppreference.com/w/cpp/la...opy_assignment. L'opérateur = a été surchargé pour effectuer une copie ou un déplacement. Les deux opérateurs peuvent exister simultanément et le programmeur est libre de désactiver avec =delete la ou les versions qui ne lui conviennene pas.

    std::unique_ptr<T> est un pointeur, ce qui peut être rendu très clair avec cette syntaxe std::unique_ptr<int> p1(new int(24)). Avec std::unique_ptr<T>, aucun opérateur de copie ou d'affectation permet d'avoirs deux instances identiques de std::unique_ptr<T> qui sont toutes deux responsable de la destruction du même pointeur.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    De manière générale, je suis tout à fait d'accord avec Bousk: le joueur n'est pas le propriétaire de la carte: au mieux, il en est seulement l'utilisateur.

    Ceci étant dit, on pourrait même aller plus loin, en estimant que le système de distribution des cartes n'est pas non plus le propriétaire des cartes qu'il distribue, qu'il n'en est aussi qu'un utilisateur.

    Il ne faut en effet jamais oublier que le principe de base à respecter en tous temps est le SRP, dont je te donne ici une adaptation:
    Tout nom (groupe nominal) dans l'analyse des besoins doit apparaitre dans le code sous la forme d'un type de donnée ou d'une donnée
    Tout verbe (groupe verbal) dans l'analyse des besoins doit apparaitre dans le code sous la forme d'une fonction
    Et, du coup, il faut faire la distinction entre le jeu de carte et le système qui permet de distribuer les cartes du jeu!

    Tu as donc trois éléments qui devraient entrer en jeu:
    1. le jeu de cartes proprement dit
    2. le "sabot" qui distribue les cartes (même si tu peux l'appeler comme tu veux)
    3. et le(s) joueurs

    Parmi ces trois éléments, seul le jeu proprement dit est le propriétaire légitime des cartes, et c'est donc le seul élément qui puisse, effectivement, les manipuler sous la forme de std::unique_ptr.
    Les deux autres éléments (le sabot et le(s) joueur(s)) n'étant que des utilisateurs des cartes, et, à ce titre, ils devraient ne manipuler les cartes qu'au travers de std::reference_wrapper.

    De cette manière, tu pourras "remplir" le sabot avec "autant de jeux" que tu le souhaites (les sabots de black jack, au casino, contiennent souvent entre trois et cinq jeux entiers !!!), et transférer les cartes du sabot vers le joueur (ou d'un joueur vers un autre) autant que tu veux, sans risquer de modifier la structure même du jeu de carte en lui-même
    je verrais bien une structure proche de celle-ci pour résoudre ton problème:
    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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    /* Pour jouer aux cartes, on a besoin ... de cartes ... */
    class Card{
        /* tu y mets ce que tu veux ici, mais les cartes auront sémantique d'entité
         * car tu as les cartes "classiques", les tarots, et plein d'autres sortes de
         * jeux de cartes ;)
         */
    public:
        Card() = default;
        Card(Card const &) = delete;
        Card & operator = (Card const &) = delete;
        virtual ~Card() = default;
    };
    /* Le système de distribution des cartes */
    class Distributor{
        /* pour la facilité, parce que le nom complet est très long */
        using stack_t = std::vector<std::reference_wrapper<const Card>>;
    public:
        /* lui aussi, il a sémantique d'entité, 
         * même si il n'est pas destiné à être dérivé
         */
        Distributor() = default;
        Distributor(Distributor const &) = delete;
        Distributor& operator = (Distributor const &) = delete;
        ~Distributor() = default;
        /* Le distributeur va agir comme une collection classique,
         * en permettant 
         * - l'ajout
         * - l'accès à celle qui se trouve "en haut" de la pile
         * - la suppression de celle qui se trouve en haut de la pile
         * - la suppression de toutes les cartes
         * - le mélange des cartes qu'il contient (car cela peut être utile :D )
         * - de savoir s'il contient ne serait-ce qu'une carte
         */
        void push(Card const & card){
            stack_.emplace_back(card);
        }
        Card const & top(){
            assert(current_ < stack_.size() && "Distributor is empty!");
            return stack_[current_].get();
        }
        void pop(){
            assert(current_ < stack_.size() && "Distributor is empty!");
            ++ current_;
        }
        void clear(){
            current_ = 0;
            stack_.clear();
        }
        bool empty() const{
            return stack_.empty() || current_ >= stack_.size();
        }
        void shuffle(){
            std::random_device rd;
            std::mt19937 g(rd());
            std::shuffle(stack_.begin(), stack_.end(), g);
        }
    private:
        stack_t stack_;
        size_t current_{0};
    };
    /* Nous avons les "jeux" (paquets) de cartes ...
     * ce sont les propriétaires des cartes qu'ils contiennent
     */
    class CardPack{
        /* par facilité */
        using pack_t = std::vector<std::unique_ptr<Card>>;
    public:
        /* ils ont -- forcément -- sémantique d'entité
         */
        CardPack(); // il construisent les cartes dont ils ont besoin ici
        CardPack(CardPack const &) = delete;
        CardPack & operator = (CardPack const &) = delete;
        virtual ~CardPack() = default;
        /* Tout ce que l'on attend de leur part, c'est d'être en mesure
         * de placer les cartes dans le sabot
         */
        void fillDistributor(Distributor & d){
            assert(!inDistributor_ && "Already put in a distributor!");
            for(auto const & it : pack_){
                d.push(*it.get());
            }
            inDistributor_ = true;
        }
    private:
        bool inDistributor_{false};
        pack_t pack_;
    };
    /* Et, enfin, on a le joueur */
    class Gamer{
        /* par facilité */
        using hand_t  = std::vector<std::reference_wrapper<const Card>>;
    public:
        /* il a -- forcément -- sémantique  d'entité */
        Gamer() = default;
        Gamer(Gamer const &) = delete;
        Gamer & operator = (Gamer const & ) = delete;
        virtual ~Gamer() = default;
        /* il peut piocher une carte dans le sabot */
        bool draw(Distributor & d){
            if(! d.empty()){
                auto const & card = d.top();
                d.pop();
                hand_.emplace_back(card);
                return true;
            }
            return false;
        }
        /* il pourra sans doute faire d'autres choses...
         * avec toutes ses cartes ... à  toi de les définir 
         */
    private:
        hand_t hand_;
    };
     
    /* enfin, il y a le jeu en lui-même, qui est le propriétaire légitime:
     * - de tous les paquets de cartes utilisés
     * - de tous les joueurs en présence
     * - de tous les sabots mis à disposition
     */
    class Game{
    public:
        /*quelques fonctionalités intéressantes */
        /* ajouter un paquet de cartes */
        void addPack(){
            packs_.emplace_back(std::make_unique<CardPack>());
            /* on choisi le distributeur dans lequel placer le paquet créé 
             * (à toi de voir comment faire)
             */
            auto & pack = packs_.front();
            pack->fillDistributor(chosenDistributor);
        }
        /* et bien sur, il doit s'exécuter */
        void run(){
            /* ... */
        }
        /* et tout le reste, bien sur ! */
    private:
        std::vector<std::unique_ptr<Distributor>> distributors_;
        std::vector<std::unique_ptr<CardPack>> packs_;
        std::vector<std::unique_ptr<Gamer>> gamers_;
    };
    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

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

Discussions similaires

  1. Réponses: 4
    Dernier message: 02/08/2007, 13h37
  2. [VBA-E] Transfert de données d'un répertoir à un autre
    Par anisr dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 26/04/2007, 10h55
  3. [VBA]Transfert d'une zone de liste à une autre
    Par Herman dans le forum VBA Access
    Réponses: 2
    Dernier message: 13/04/2007, 14h24
  4. transfert de données d'une table à l'autre
    Par VIRGINIE87 dans le forum Access
    Réponses: 12
    Dernier message: 06/03/2007, 07h48
  5. Réponses: 3
    Dernier message: 12/01/2007, 16h23

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