1
votes

Comment le constructeur de copie s'exécute-t-il?

J'essaie de comprendre le constructeur de copie en détail. Ce faisant, j'ai fait l'exemple suivant,

#include<iostream>

class Test
{
private:
        int a;
public:
        /*
        Test(const Test &t)                          // User defined copy constructor
        {
            a = t.a;
        } */
        Test()
        {
                a = 120;
        }
        int display()
        {
                return a ;
        }
        void set(int var)
        {
                a = var;
        }
};

int main()
{
        Test t1;
        std::cout << "t1.a " << t1.display() << std::endl;
        Test t2 = t1;                                           //Default copy constructor is called
        std::cout << "T2.a " << t2.display() << std::endl;
        t2.set(99);                                             //Changing the value
        std::cout << "t1.a " << t1.display() << std::endl;
        std::cout << "T2.a " << t2.display() << std::endl;
        return 0;
}

J'ai lu en ligne que le constructeur de copie par défaut fait une "copie superficielle" Donc cela signifie que si obj1 = obj2, ce que je change jamais dans obj1 ou obj2 même après la mission doit être réfléchi à la fois sur l'objet car ils pointent vers le même emplacement. Mais dans cet exemple, lorsque je change la valeur d'un objet, cela ne se reflète pas dans l'autre. Le même résultat est obtenu lors de l'utilisation du constructeur de copie défini par l'utilisateur.

Quelqu'un peut-il clarifier ce sujet, que la copie superficielle se produise ou non!

Merci!


5 commentaires

la copie superficielle pour int est une copie profonde. Essayez d'avoir int * a au lieu de int a


Le constructeur de copie par défaut effectue une copie bit à bit .


Vous avez été mal informé. Une copie superficielle ne signifie pas que obj1 = obj2 fait que obj1 et obj2 font référence au même emplacement. Une copie superficielle signifie que tous les membres qui sont des pointeurs sont copiés par valeur, plutôt que de créer une copie séparée de ce que les pointeurs pointent. La distinction entre copie profonde et superficielle n'a d'importance que si l'objet contient des pointeurs - ce que votre exemple ne fait pas.


@ user207421 - un constructeur de copie "par défaut" effectue une copie par membre. Pour les types de base comme int et pour les pointeurs qui se trouvent être une copie au niveau du bit. Pour chaque membre (ou base) qui a un constructeur de copie accessible, il utilise ce constructeur de copie.


Regardez-le d'un point de vue technique. Pour l'ordinateur, un objet est un ensemble de nombres. Comme la ligne d'une table, sans étiquette de nom attachée, aucune idée de la fin de chaque cellule et aucune connaissance de ce que les données sont censées être. Cela pourrait être des données entières, des valeurs char, vous ne savez pas sans connaître la classe. Ces données sont copiées sur un constructeur de copie par défaut. Les pointeurs sont particulièrement intéressants ici: ce sont des adresses vers une mémoire. Leurs données brutes sont cette adresse. Que vous copiez, ce qui signifie qu'après une telle copie, les deux objets ont la même adresse stockée dans ce pointeur.


4 Réponses :


1
votes

Considérez la copie superficielle comme une simple affectation. Donc,

t2.a = t1.a

signifie

Test t2 = t1; 

Puisque a est un int , si vous modifiez a , à partir de t1 , il ne se reflétera pas dans t2 . Donc, pour int une copie superficielle est en effet une copie profonde.

Considérez le cas où a était de type int * . Désormais, t2.a et t1.a pointent tous les deux vers les mêmes emplacements mémoire. Donc, si vous modifiez la valeur à l'emplacement mémoire t1.a , la même chose serait reflétée via t2.a car ils pointent en fait vers le même emplacement.


0 commentaires

3
votes

Une copie superficielle n'est pas quelque chose de spécial dont vous devez vous souvenir en règle générale. Au lieu de cela, il s'agit simplement de quelque chose qui se produit à la suite de l'utilisation d'une référence ou d'un pointeur. COnsemblez cet exemple:

struct foo { 
     int* x;
};

int a = 4;
foo f{&a};

Ici x pointe vers a et si vous copiez f les nouvelles instances x pointera sur le même a . C'est une copie superficielle. Une copie complète consisterait à respecter que non seulement x , mais aussi ce vers quoi x pointe, fait partie intégrante de foo et doit être copié aussi. En général, le compilateur ne peut pas décider ce que vous voulez. x est-il juste une référence, ou ce que x fait-il également partie de foo ? Par conséquent, ce que vous obtenez est évident: seuls les membres sont copiés, pas ce à quoi ils pourraient se référer.

Maintenant, si vous copiez le toto puis modifiez la valeur pointée par x , alors cela modifiera le même a code >. Une copie superficielle a été faite. D'après mon expérience, les termes copie profonde et superficielle ajoutent plutôt de la confusion que de la clarté. Ce que vous obtenez gratuitement, c'est que tous les membres sont copiés (qu'il s'agisse d'une copie superficielle ou profonde). Seulement si vous avez besoin de plus (copiez également la pointee), vous devez vous soucier des copies superficielles par rapport aux copies profondes.

TL; DR: Il n'y a pas de copie profonde / superficielle dans votre exemple. Pour les valeurs, cette distinction n'a pas de sens. Utilisez un int * pour voir l'effet.


0 commentaires

0
votes

Le constructeur de copie implicitement défini d'une classe copie simplement chaque membre [ class.copy.ctor] / 14 . Copier les membres signifie essentiellement que chaque membre du nouvel objet (celui dans lequel est copié) est initialisé à partir du membre correspondant de l'objet copié de la même manière que si vous aviez écrit

T member(original.member);

T est le type du membre et original est l'objet en cours de copie. Si membre est de type classe, cela revient en fait à appeler le constructeur de copie de T . Cependant, votre membre est un simple int , qui n'est pas un type de classe. Il n'y a pas de constructeur de copie à appeler ( int n'a pas de constructeur de copie); le int dans le nouvel objet est simplement initialisé à partir du int de l'objet original, ce qui revient à copier la valeur du int original dans le nouveau int

Votre compilateur ne fait pas la distinction entre copie profonde et copie superficielle, il ne sait même pas ce que serait censé faire "copie profonde" ou "copie superficielle" moyenne. Il n'y a pas de copie profonde ou de copie superficielle en C ++ au niveau du langage. Ce sont simplement des termes couramment utilisés par les programmeurs pour parler de différentes approches pour copier des objets qui logiquement (mais pas physiquement) contiennent d'autres objets…


0 commentaires

2
votes

J'ai lu en ligne que le constructeur de copie par défaut fait une "copie superficielle"

Ce n'est pas la bonne façon de penser du constructeur de copie. Le constructeur de copie par défaut copie simplement les membres du type comme s'il appliquait à son tour le constructeur de copie sur les membres.

De la référence:

Si le constructeur de copie déclaré implicitement n'est pas supprimé, il est défini (c'est-à-dire qu'un corps de fonction est généré et compilé) par le compilateur si utilisé par odr. Pour les types d'union, la copie implicitement définie constructeur copie la représentation de l'objet (comme par std :: memmove). Pour types de classe non-union (classe et struct), le constructeur effectue copie complète par membre des bases et des membres non statiques de l'objet , en leur ordre d'initialisation, en utilisant l'initialisation directe.

Cela ressemble donc plus à une copie profonde qu'à une copie superficielle.


0 commentaires