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 :

Empêcher l'assignation et la copie


Sujet :

C++

  1. #1
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut Empêcher l'assignation et la copie
    En attendant unique_ptr<> (sic ), comment empêche l'assignation et la copie d'un objet ?
    Suffit-il de déclarer le copy constructor et l'operator= en private ?

  2. #2
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Citation Envoyé par camboui Voir le message
    En attendant unique_ptr<> (sic ), comment empêche l'assignation et la copie d'un objet ?
    Suffit-il de déclarer le copy constructor et l'operator= en private ?
    D'après que je sache, oui
    C'est la technique utilisée par boost noncopyable

  3. #3
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    Une petite précision: il "suffit" de déclarer le constructeur par copie et l'opérateur d'assignation private, mais de ne surtout pas les définir

    Autrement, tu placera certes une restriction à la copie et à l'assignation, mais tu ne l'interdira pas forcément
    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

  4. #4
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Pour forcer le linker à ne pas construire l'éxecutable ?

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par camboui Voir le message
    Pour forcer le linker à ne pas construire l'éxecutable ?
    Plutôt pour forcer le linker à sortir sur une erreur si, par malheur, tu venais effectivement à invoquer un comportement de copie ou d'assignation

    Car, si tu n'invoque jamais un comportement de copie ou d'assignation, bien que le compilateur ait estimé que ces comportements "existent", le linker ne se plaindra pas

    Tout ce qui peut t'arrêter avant la période d'utilisation (AKA au moment de la compilation et / ou de l'édition de liens) est de nature à t'éviter des soucis parfois difficiles à résoudre en période d'utilisation
    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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Si je passe par une nouvelle réponse, c'est parce que je prévois qu'elle va se transformer en roman...

    Pour comprendre l'idée, considérons les choses depuis le début:

    Si tu ne déclares pas l'un des "big fours" (constructeur, constructeur par copie, opérateur d'assignation et destructeur), le compilateur va les implémenter en accessibilité publique, ce qui les rend tout à fait accessibles de "n'importe où dans le code".

    Si tu estimes qu'une classe ne peut pas être copiée ni / ou assignée, tu as tes raisons qui sont surement valables et qui n'ont pas à être remises en question.

    Cependant, si tu déclares le constructeur par copie et / ou l'opérateur d'assignation en accessibilité privée et que tu les définis (que tu les implémente, si tu préfères), un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void foo()
    {
        /* une instance "non copiée" */
        MaClass mc:
        /* tu as oublié que MaClasse n'est pas sensée être copiable */
       MaClass deux(mc);
    }
    Le compilateur se plaindra parce que "le constructeur par copie est privé dans ce contexte"...

    Tu as déjà avancé d'un grand pas

    Mais si tu fais l'erreur dans une fonction membre de la classe, cela devient beaucoup plus dangereux...

    Ainsi un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void MaClass::doSomething()
    {
        /* tu oublie que ta classe n'est pas sensée etre copiable */
        MaClass temp(*this);
        /*...*/
    }
    sera, non seulement accepté par le compilateur (après tout, MaClass(MaClass const&) est déclarée ) mais sera aussi acceptée par l'éditeur de lien (car le compilateur a bel et bien créé le code exécutable correspondant)...

    Et, comme tu as surement des raisons pour avoir rendu ta classe non copiable, tu risque de te rendre compte du problème que... bien loin de l'invocation de doSomething...

    La recherche de la raison pour laquelle ton instance de MaClass part en vrille deviendra une véritable galère... et occasionnera, au minimum un délais supplémentaire avant la remise du projet, au pire... le lancement d'une bombe atomique (allez, soyons sympa: un plantage lors de la démonstration de remise au client... ce qui fait quand même tache )

    Par contre, si tu n'implémente pas le constructeur par copie et / ou l'opérateur d'affectation que tu as rendu privé(s), le linker te jettera lorsqu'il travaillera sur doSomething au motif qu'il rencontre une
    référence indéfinie à <signes cabalistiques>MaClass(MaClass const&)<signes cabalistiques>
    C'est à dire, bien avant d'arriver chez le client

    En plus, avec un peu de chance, il t'indiquera même dans quelle fonction tu dois aller chercher cet appel pour lequel il ne trouve aucun symbole

    Tu sécurise donc à très peu de frais (et même en gagnant le temps de l'implémentation des deux méthodes) ta conception générale
    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

  7. #7
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Merci beaucoup pour la réponse exhaustive (bien qu'elle n'était pas nécessaire) on apprécie

  8. #8
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    A propos des "big fours" comme tu dis, j'en en un peu marre de les taper à tout bout de champ, alors que leur écriture est bien souvent récurrente et systématique: Copie/assignation des membres un à un, précédé éventuellement par un appel au parent quand il y a héritage. J'ai cru comprendre qu'il allait y avoir un nouveau mot clé "default" pour ne plus se taper cette... tape

  9. #9
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Oui, il va y avoir un nouveau mot clé.

    Par contre, je ne les tape jamais:
    - soit mes membres sont implicitement copiables
    - soit je manipule des entités, et donc dériver d'une classe noncopyable me suffit.
    - soit je passe par un "wizard" de mon éditeur (qui génère des squelettes de casses à partir de 4 sémantiques types)

    Pour unique_ptr, il sera implicitement déplaçable au point qu'il faille en plus dériver de noncopyable ?
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par camboui Voir le message
    Merci beaucoup pour la réponse exhaustive (bien qu'elle n'était pas nécessaire) on apprécie
    A vrai dire, j'aime avoir la certitude que les gens auront compris la raison de mes conseils...

    Ca permet d'assurer une certaine "fixation dans le temps" de ce qu'on leur a expliqué

    Citation Envoyé par camboui Voir le message
    A propos des "big fours" comme tu dis, j'en en un peu marre de les taper à tout bout de champ, alors que leur écriture est bien souvent récurrente et systématique: Copie/assignation des membres un à un, précédé éventuellement par un appel au parent quand il y a héritage. J'ai cru comprendre qu'il allait y avoir un nouveau mot clé "default" pour ne plus se taper cette... tape
    N'oublie pas que, si le comportement par défaut te suffit, tu peux parfaitement décider de, tout simplement, ne pas les déclarer et laisser le soin au compilateur de se les "palucher" :

    • Si ta classe est défaut constructible et n'utilise que des membres qui sont eux-même défaut constructible, tu peux "laisser tomber" la déclaration et l'implémentation du constructeur
    • Si ta classe est copiable et qu'elle utilise des membres qui sont eux-même copiables, tu peux te passer de la déclaration et de l'implémentation du constructeur par copie
    • Si ta classe est assignable et qu'elle utilise des membres qui sont eux-même assignables, tu peux te passer tu peux te passer de la déclaration et de l'implémentation de l'opérateur d'affectation
    • Si, enfin, le destructeur ne fait rien d'autre que d'appeler le destructeur des membres de ta classes (hors allocation dynamique), tu peux te passer de la déclaration et de l'implémentation du destructeur

    L'exception est le destructeur dans le cadre de l'héritage: même s'il ne fait rien de particulier, tu le déclarer (selon le cas) virtuel et public ou non virtuel et protégé, et donc l'implémenter

    Pour le reste, il est malheureusement vrai que, si tu décide - par besoin - de redéfinir le comportement du constructeur par copie, de l'opérateur d'affectation ou du destructeur (pour une raison autre que le simple fait qu'il est virtuel), tu devras le plus souvent aussi envisager de redéfinir le deux autres
    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

  11. #11
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    A propos des "big fours" comme tu dis, j'en en un peu marre de les taper à tout bout de champ, alors que leur écriture est bien souvent récurrente et systématique: Copie/assignation des membres un à un, précédé éventuellement par un appel au parent quand il y a héritage.
    Ha ! C'est une des raisons qui me poussent à coller des shared_ptr partout dès que possible (je suis feignant). Plus besoin d'écrire de constructeur de copie/opérateur d'assignement avec des shared_ptr membres vu que le comportement par défaut, la copie membre à membre, marche comme sur des roulettes.

  12. #12
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    D'ailleurs à ce propos est ce que la régle du : on n'implémente le BIG four que dans le cas où il y'a des alloc dynamique (ie gestion de pointeurs) est vrai. et que donc dans le cas de classes ne gérant que des variables déclarés sur la pile les ctor et opérateur d'assignation fournies par le compilateur est suffisant, est une règle applicable en tout lieu?

    Je me demande ça car j'épluche pas mal le code de boost en ce moment et je me rends compte que généralement il y'a un commentaire disant que le code généré par le compilo est suffisant.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  13. #13
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Tout dépend des sémantiques et des membres.
    Cf l'article "Big Rule of Two" qui raffine la règle pour les classes valeur qui ont des membres non trivialement copiables.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  14. #14
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Hum merci .


    ps : l'article est bien celui d'artima?
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  15. #15
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Oui. C'est bien celui là.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  16. #16
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Luc Hermitte Voir le message
    Pour unique_ptr, il sera implicitement déplaçable au point qu'il faille en plus dériver de noncopyable ?
    Il n'y aura pas de constructeur par déplacement ou d'opérateur égal par déplacement (j'ai du mal à lui trouver un nom que j'aime bien à celui-là) généré automatiquement par le compilateur. Donc avoir un membre de type unique_ptr rendra la classe non copiable par défaut, tout comme avoir un membre constant, ou un membre référence.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  17. #17
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Je crois que je vais devoir revoir certains concepts de base que j'ai oublié (ou ignoré) à propos des "big fours". J'y reviendrai probablement dans un autre thread.

    Pour revenir à la copie et l'assignation interdites, que se passe-t-il pour les classes héritées ? Par défaut, je suppose qu'elles héritent de la même "tare", càd ni copie ni assignation puisque l'accès aux méthodes correspondantes de la classe parente sera interdit.

  18. #18
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Ouai, d'ailleurs il est pas rare de voir dans des projets une class nonCopyable déclaré juste comme ça et ainsi quand ils veulent rendre une class non copiable ils héritent de nonCopyable.



    Il me semble _mais j'y mettrais pas ma main au feu_ que certains projets de boost sont fait comme ça.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  19. #19
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Oui et non :
    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
     
    class A
    {
    public:
       A(){}
    private:
       A(const A&);
       A& operator=(const A&);
    };
     
    class B :public A
    {
    public:
       B(){}
       B(const B&){}
       B& operator=(const B&){return *this;}
    };
     
    class C :public A
    {
    public:
       C(){}
    };
     
    int main()
    {
       B b;
       B b2(b);//OK
       b2 = b; // OK
     
       C c;
       C c2(c); // Erreur
       c2 = c; // Erreur
     
       return ;
    }
    boost::noncopiable est basé là dessus.

  20. #20
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Ben ouaip si tu les redéfinies dans la classe dérivée ça marche. J'aurais du préciser
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

Discussions similaires

  1. copy constructor/ assignement/ destructor
    Par lafrankette dans le forum Débuter
    Réponses: 5
    Dernier message: 24/08/2009, 18h09
  2. Réponses: 1
    Dernier message: 12/09/2007, 16h17
  3. Conseil: empêcher copie de l'application
    Par GymTonic dans le forum Access
    Réponses: 4
    Dernier message: 26/03/2006, 10h32
  4. Empêcher la copie d'un prog pendant son exécution
    Par JuanLopez1966 dans le forum Assembleur
    Réponses: 3
    Dernier message: 09/09/2004, 19h49

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