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 :

Constructeur par recopie


Sujet :

Langage C++

  1. #1
    Nouveau membre du Club
    Inscrit en
    Septembre 2008
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 29
    Points : 27
    Points
    27
    Par défaut Constructeur par recopie
    Bonjoru tout le monde.

    Dans un programme plus vaste, j'ai un problème avec un constructeur/desctructeur. Le crash vient au moment de libérer la mémoire quand tous les destructeurs sont appelés en chaine.

    (je ne mets pas tous les include pour na pas polluer, include "string.h", include des classes.h)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    //Programme principale
    void main()
    {
        CTestB loc_test_b = CTestB();
        {
            CTest loc_test2 = CTest(loc_test_b);
     
            //delete loc_test2; test effectué en faisant un new a la ligne précédente
        }
    };
    Classe principale encapsulant un sous classe B
    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
     
    class CTest
    {
    public:
        CTest();
        CTest(CTestB&);
        ~CTest();
    private:
        CTestB m_test_B;
    };
     
    //Code
    CTest::CTest()
    {
        m_test_B = CTestB();
    }
     
    CTest::CTest(CTestB& par_test_b)
    {
        m_test_B = CTestB(par_test_b);
    }
     
    CTest::~CTest()
    {
        //delete m_test_B;
    }
    Contenu de la classe b :
    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 CTestB
    {
    public:
        CTestB();
        ~CTestB();
     
        CTestB(const CTestB& par_cls_testB);
        char* mGetAddr() const {return addr;};
    private:
        char *addr;
     
    };
     
    //CODE
    CTestB::CTestB()
    {
        addr = new char[20];
        strncpy(addr, "init", 5);
    }
     
    CTestB::CTestB(const CTestB& par_cls_testB)
    {
        addr = new char[20];
        strncpy(addr, par_cls_testB.mGetAddr(), 5);
        strncat(addr, "copy", 5);
    }
     
    CTestB::~CTestB()
    {
        if(addr != NULL)
        {
            delete addr;
            addr = NULL;
        }
    }
    Le problème survient au delete addr. Ce pointeur a déjà été libéré ... Grrr, l'erreur doit être basique mais je m'y perd.

    Quelqu'un voit il mieux que moi ?

  2. #2
    screetch
    Invité(e)
    Par défaut
    regle des 3 :
    si tu as besoin d'un detructeur non trivial, ou bien d'un constructeur de copie non trivial, ou bien d'un opérateur d'affectation non trivial, alors tu as surement besoin des 3

    il te manque l'opérateur =

  3. #3
    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
    Pour plus d'info sur la règle des trois/forme orthodoxe de Coplien/Règle des Big Four, voir aussi la faq

  4. #4
    Nouveau membre du Club
    Inscrit en
    Septembre 2008
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 29
    Points : 27
    Points
    27
    Par défaut
    Merci beaucoup mais comme a chaque fois que je repose a plat mon problème je trouve souvent la solution...

    Est ce que la suite est vrai ?

    En sortie du constructeur CTest, la donnée m_Test_B qui est locale est détruite. Le pointeur addr est donc désalloué.
    Ensuite dans le programme principale, quand on sort du bloc où CTest est construit(juste après le }), le destructeur est automatiquement appelé. Le destructeur de CTest appelle le destructeur de CTestB qui tente de désalloué une nouvelle fois addr.

    J'ai donc changer CTestB m_test_B en CTestB* m_test_B et fait les allocation correspondantes.

  5. #5
    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
    Citation Envoyé par CSIE_Angel#5 Voir le message
    En sortie du constructeur CTest, la donnée m_Test_B qui est locale est détruite. Le pointeur addr est donc désalloué.
    m_Test_B est membre de CTest. Donc m_Test_B ne sera détruit que quand l'objet CTest sera détruit et non en sortie du contructeur.
    Citation Envoyé par CSIE_Angel#5 Voir le message
    Ensuite dans le programme principale, quand on sort du bloc où CTest est construit(juste après le }), le destructeur est automatiquement appelé. Le destructeur de CTest appelle le destructeur de CTestB qui tente de désalloué une nouvelle fois addr.
    Cf commentaires dans le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        CTestB loc_test_b = CTestB(); // Première instance
        {
            CTest loc_test2 = CTest(loc_test_b); // Copie de loc_test_b dans loc_test2.m_Test_B 
        } // destruction de loc_test2 qui détruit m_Test_B qui libère addr 
    // sortie du main : destruction de loc_test_b qui libère (une seconde fois) addr
    Citation Envoyé par CSIE_Angel#5 Voir le message
    J'ai donc changer CTestB m_test_B en CTestB* m_test_B et fait les allocation correspondantes.
    Définir correctement l'opérateur = et le constructeur par copie sont une meilleur solution que de réintroduire un pointeur qui te provoquera le même genre de problème plus tard. Cf F.A.Q Comment gérer proprement des allocations / désallocations de ressources ? Le RAII !, les entrées de la F.A.Q proposées par Arzar ci-dessus et les Pointeurs intelligents.

  6. #6
    screetch
    Invité(e)
    Par défaut
    non, m_test_B n'est pas locale, elle appartient a (this) et (this) vit encore.

    cette ligne: ne fait pas ce que tu penses.
    Elle crée un objet temporaire TestB (grace au contructeur par defaut),
    puis appelle l'opérateur d'affectation pour ecraser le contenu de m_test_B, puis efface le temporaire qui n'est plus utilisé.

    Ce qui se passe c'est que tu appelles l'opérateur =. or tu n'as pas ecrit cet opérateur; son implémentation par défaut va etre de copier le contenu de TestB, membre a membre, donc de copier le pointeur contenant les données. On va donc avoir deux objets qui pointent vers la meme donnée; lorsque le temporaire est detruit, son destructeur va liberer la mémoire.

  7. #7
    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 CSIE_Angel#5 Voir le message
    J'ai donc changer CTestB m_test_B en CTestB* m_test_B et fait les allocation correspondantes.
    C'est vrai, mais tu as une solution incomplète, et peut-être même erronée.

    En fait, comme on te l'a indiqué, le problème fondamental vient du fait que quand tu copie un objet de type CTestB, ça se passe mal, car la copie n'est pas gérée (et tu te retrouves avec deux pointeurs à posséder la même zone mémoire, qui vont donc tous deux la libérer => boom).

    Tu as temporairement corrigé ce problème, en ne faisant plus de copie de l'objet dans ton programme, mais ce problème existe encore, et ne demande qu'à refaire surface.

    Tu as globalement deux solutions à ça :
    - Soit ça a du sens de copier CTestB, et tu lui fourni un constructeur de copie (la règle des 3 citée dans la FAQ), après que fait ce constructeur ? A priori il recopie le buffer. Si ça a du sens que plusieurs copies de CTestB se partagent la même zone mémoire, tu remplaces le pointeur sur cette zone par un pointeur intelligent qui gère le partage (et du coup, plus besoin de destructeur, ni de constructeur de copie explicite).

    - Soit ça n'a pas de sens de copier CTestB, et dans ce cas, il vaut mieux interdire la copie, afin d'éviter qu'elle ait lieu par inadvertance comme c'était le cas dans ton code : Tu déclares le constructeur de copie en privé, et tu ne l'implémente même pas.
    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.

  8. #8
    Nouveau membre du Club
    Inscrit en
    Septembre 2008
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 29
    Points : 27
    Points
    27
    Par défaut
    Ok, je vais prendre en compte la remarque des 3 !

    mais une question en suivant :
    quand j'écris et que j'ai défini un constructeur par copie et l'opérateur = :
    Quelles sont les fonctions réellement appelées :
    1°) Appel au constructeur par défaut CTestB
    2°) puis appel de operator= (dans lequel je ferais certainement une copie du contenu du pointeur addr)
    3°) destruction de l'objet créé en 1°) ?


    Mais dans ce cas que fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    m_test_B = CTestB(par_test_b);
    1°) appel du constructeur par copie
    2°) ??? y'a t'il un appel a l'opérateur = ?

    merci

  9. #9
    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
    Un petit bout de code pour comprendre :
    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
    #include <iostream>
     
    class A
    {
    public:
       A()
       {
          std::cout<<"constructeur par defaut"<<std::endl;
       }
       A(A const&)
       {
          std::cout<<"construction par copie"<<std::endl;
       }
       A&operator=(A const&)
       {
          std::cout<<"operateur = "<<std::endl;
          return *this;
       }
       ~A()
       {
          std::cout<<"destruction"<<std::endl;
       }
    };
     
    int main()
    {
       {
          std::cout<<"Test 1 "<<std::endl;
          A a;
          std::cout<<"Fin Test 1 "<<std::endl;
       }
     
       {
          std::cout<<"Test 2 "<<std::endl;
          A a2 = A();      
          std::cout<<"Fin Test 2 "<<std::endl;
       }
     
       {
          std::cout<<"Test 3 "<<std::endl;
          A a;
          a = A();
          std::cout<<"Fin Test 3 "<<std::endl;
       }
     
       {
          std::cout<<"Test 4 "<<std::endl;
          A a;
          A a2 = A(a);
          A a3 = a;
          std::cout<<"Fin Test 4 "<<std::endl;
       }
     
       {
          std::cout<<"Test 5 "<<std::endl;
          A a;
          A a2;
          a2 = A(a);
          A a3;
          a3 = a;
          std::cout<<"Fin Test 5 "<<std::endl;
       }
       return 0;
    }
    Sortie produite :
    Test 1
    constructeur par defaut
    Fin Test 1
    destruction
    Test 2
    constructeur par defaut
    Fin Test 2
    destruction
    Test 3
    constructeur par defaut
    constructeur par defaut
    operateur =
    destruction
    Fin Test 3
    destruction
    Test 4
    constructeur par defaut
    construction par copie
    construction par copie
    Fin Test 4
    destruction
    destruction
    destruction
    Test 5
    constructeur par defaut
    constructeur par defaut
    construction par copie
    operateur =
    destruction
    constructeur par defaut
    operateur =
    Fin Test 5
    destruction
    destruction
    destruction

  10. #10
    screetch
    Invité(e)
    Par défaut
    oui, puisque tu l'as ecrit =)

    pour ne pas appeler l'opérateur = il suffit de ne pas le taper

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    CTestB m_test_B(par_test_b); // créé directement comme une copie
    m_test_B = CTestB(par_test_b); // tu as ecrit "=" a mon avis il va l'appeler ^^

  11. #11
    screetch
    Invité(e)
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Un petit bout de code pour comprendre :[...]
    c'est un tres bon exemple, et je pinaille, mais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
       std::cout<<"Test 2 "<<std::endl;
       {
          A a2 = A();      
       }
       std::cout<<"Fin Test 2 "<<std::endl;
    aurait permis de mieux encadrer les destructeurs (qui font un peu partie du test je trouve)
    (pinaillage...)

  12. #12
    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
    Citation Envoyé par screetch Voir le message
    c'est un tres bon exemple, et je pinaille, mais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
       std::cout<<"Test 2 "<<std::endl;
       {
          A a2 = A();      
       }
       std::cout<<"Fin Test 2 "<<std::endl;
    aurait permis de mieux encadrer les destructeurs (qui font un peu partie du test je trouve)
    (pinaillage...)
    Pas tout à fait puisqu'ainsi on ne voit pas la différence entre le destructeur du temporaire et le destructeur de sortie de scope :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
       {
          std::cout<<"Test 3 "<<std::endl;
          A a;
          a = A();
          std::cout<<"Fin Test 3 "<<std::endl;
       }
    donne
    Test 3
    constructeur par defaut
    constructeur par defaut
    operateur =
    destruction
    Fin Test 3
    destruction
    mais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
       {
          std::cout<<"Test 3 "<<std::endl;
          A a;
          a = A();
       }
       std::cout<<"Fin Test 3 "<<std::endl;
    donne
    Test 3
    constructeur par defaut
    constructeur par defaut
    operateur =
    Fin Test 3
    destruction
    destruction
    Et on se rend moins compte que la destruction du temporaire et de a n'ont pas lieu tout à fait au même moment.
    On aurait pu faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
       {
          std::cout<<"Test 3 "<<std::endl;
          A a;
          a = A();
          std::cout<<"Fin Test 3 "<<std::endl;
       }
       std::cout<<"Sortie de la portee "<<std::endl;
    Mais j'avais peur que ça devienne illisible

  13. #13
    Nouveau membre du Club
    Inscrit en
    Septembre 2008
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 29
    Points : 27
    Points
    27
    Par défaut
    Merci beaucoup a tous.

    Je vais de ce pas mettre des fonctions temporaires de tracé dans mes constructeurs/destructeurs pour tout bien déceler.

    Et je vais interdire pour l'instant l'appel au constructeur par copie et utiliser l'opérateur =.

    Merci beaucoup a tout le monde !

  14. #14
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par CSIE_Angel#5 Voir le message
    Et je vais interdire pour l'instant l'appel au constructeur par copie et utiliser l'opérateur =.
    Je te le déconseille: Si tu interdis l'un, interdis aussi l'autre, car les deux font une copie du contenu de l'objet à un moment ou un autre (d'ailleurs, l'idiome copy-and-swap conseille d'implémenter l'opérateur = en utilisant le constructeur par copie).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  15. #15
    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
    Citation Envoyé par screetch Voir le message
    oui, puisque tu l'as ecrit =)

    pour ne pas appeler l'opérateur = il suffit de ne pas le taper

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    CTestB m_test_B(par_test_b); // créé directement comme une copie
    m_test_B = CTestB(par_test_b); // tu as ecrit "=" a mon avis il va l'appeler ^^
    Tu peux très bien 'taper' = et ne pas avoir d'appelle à l'op= :

    A a;
    A b = a;
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  16. #16
    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
    Citation Envoyé par Goten Voir le message
    Tu peux très bien 'taper' = et ne pas avoir d'appelle à l'op= :
    C'est l'objectif des couples de tests (Test 2, Test 3) et (Test 4, Test 5) : montrer que type a = XXX n'appelle pas l'opérateur = mais le constructeur - par défaut pour Test 2, de copie pour Test 4.

  17. #17
    Nouveau membre du Club
    Inscrit en
    Septembre 2008
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Septembre 2008
    Messages : 29
    Points : 27
    Points
    27
    Par défaut
    Merci beaucoup,

    j'ai implémenté l'opérateur = et le constructeur par recopie et c'est beaucoup mieux !

    Mais dans un souci de compréhension total :
    je remarque que mes 2 méthodes se ressemblent énormément !!! Quelle est donc la différence notoire entre l'utilisation d'un opérateur d'affectation et un constructeur par copie ?

    Si j'ai bien compris, on utilise un constructeur par copie quand la classe doit gérer des ressources (pointeurs, objets dynamiques, ...) ?

    Sinon puis-je écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    CTest::CTest(CTest const &par_cls_test)
    {
        //bla bla pleins de trucs a copier
        //allocation des pointeurs de la classe
        //recopie des données des pointeurs de par_cls_test dans la classe a construire
    }
     
    CTest& CTest:operator=(CTest const& par_cls_test)
    {
        this = CTest(par_cls_test); // Appel du constructeur par copie ?
        return *this;
    }

  18. #18
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Tu ne peux pas faire ton opérateur = ainsi, tu dois utiliser l'idiome copy-and-swap:
    Code C++ : 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
    CTest::CTest(CTest const &par_cls_test)
    {
    	//bla bla pleins de trucs a copier
    	//allocation des pointeurs de la classe
    	//recopie des données des pointeurs de par_cls_test dans la classe a construire
    }
     
    void CTest::Swap(CTest &autre)
    {
    	//Échanger toutes les variables
    	//On peut utiliser std::swap<> pour ça.
    }
     
    CTest& CTest::operator=(CTest tmp) //Copie temporaire directement en paramètre
    {
    	Swap(tmp);
    	return *this;
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  19. #19
    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
    Citation Envoyé par CSIE_Angel#5 Voir le message
    Mais dans un souci de compréhension total :
    je remarque que mes 2 méthodes se ressemblent énormément !!! Quelle est donc la différence notoire entre l'utilisation d'un opérateur d'affectation et un constructeur par copie ?
    La différence notoire c'est que le constructeur par copie, comme son nom l'indique construit un nouvel objet en copiant un objet source, alors que l'opérateur d'affectation copie sur un objet déjà existant.

    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    A a1;
    A a2(a1):// constructeur par copie
    A a3 = a1; // constructeur par copie
    Alors que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    A a1;
    A a2;
    a1 = a2;
    appelle l'opérateur=
    Edit : Et donc concrètement l'opérateur= est un peu plus difficile à écrire car il faut généralement faire attention à libérer proprement la ressource que possède déjà l'objet, vu qu'il a déjà été construit avant de venir en allouer une nouvelle et copier.

  20. #20
    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
    Citation Envoyé par CSIE_Angel#5 Voir le message
    je remarque que mes 2 méthodes se ressemblent énormément !!! Quelle est donc la différence notoire entre l'utilisation d'un opérateur d'affectation et un constructeur par copie ?
    Sur l'implémentation, voir la réponse précédente de Médinoc et l'entrée de la F.A.Q. Comment écrire un opérateur d'affectation correct ?. Médinoc propose un passage par valeur et la F.A.Q. un passage par référence constante : la solution de Médinoc bénéficie de la (N)RVO). La F.A.Q sera probablement rafraîchie sur ce point lors d'une prochaine maj.

    Sur l'utilisation :
    -> le constructeur par copie permet de construire directement un objet à partir d'un objet existant. L'objet construit n'existe pas avant l'appel du constructeur de copie et n'est pas d'abord construit à un état par défaut ou invalide. Il est directement construit à partir de la copie donnée en paramètre. L'appel au constructeur de copie peut être explicite, lors du passage d'un paramètre, lors du retour d'une fonction, etc.
    -> l'opérateur d'affectation prend un objet déjà existant et valide et change son état interne avec l'objet donnée en paramètre. Avant l'assignation, l'opérande de droite était un objet déjà construit avant. L'opérateur est appelé explicitement lorsque on écrit a=b ou implicitement pour les membres d'un type dont l'opérateur est généré implicitement.
    A noter que la déclaration suivante :
    est équivalente à
    [EDIT] : grillé par Arzar Cf sa réponse.

    Citation Envoyé par CSIE_Angel#5 Voir le message
    Si j'ai bien compris, on utilise un constructeur par copie quand la classe doit gérer des ressources (pointeurs, objets dynamiques, ...) ?
    Oui et non. Comme indiquer dans la F.A.Q (ici et suivantes), si tu ne définis pas de constructeur de copie, d'opérateur = ou de destructeur, le compilateur génère une version par défaut. Il est recommandé de ne pas définir une de ces fonctions qui ferait exactement la même chose que la version par défaut (modulo le destructeur virtuel si besoin de polymorphisme).

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 11
    Dernier message: 25/08/2006, 17h00
  2. Constructeur par recopie
    Par Bebert71 dans le forum C++
    Réponses: 13
    Dernier message: 18/05/2006, 16h08
  3. [Débutant] Constructeur par recopie pour TComponent
    Par Runlevel dans le forum C++Builder
    Réponses: 9
    Dernier message: 06/05/2006, 17h58
  4. Constructeur par recopie
    Par KernelControl dans le forum C++
    Réponses: 2
    Dernier message: 29/12/2005, 13h24
  5. Constructeur par recopie
    Par sdebrois dans le forum Composants VCL
    Réponses: 13
    Dernier message: 21/10/2004, 15h47

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