2008-10-19 7 views
5

Dites que j'ai une structure "s" avec une variable de membre int pointeur "i". J'alloue de la mémoire sur le tas pour i dans le constructeur par défaut de s. Plus tard dans une autre partie du code, je passe une instance de s par valeur à une fonction. Est-ce que je fais une copie superficielle ici? Supposons que je n'ai pas implémenté de constructeur de copie ou d'opérateur d'affectation ou quoi que ce soit pour s ... juste le constructeur par défaut.Question à propos de la copie superficielle en C++

Répondre

8

Pour faire suite à ce que @ [don.neufeld.myopenid.com] a dit, ce n'est pas seulement une copie superficielle, mais c'est soit (faites votre choix) une fuite de mémoire ou un pointeur qui pend.

// memory leak (note that the pointer is never deleted) 
class A 
{ 
    B *_b; 
    public: 
    A() 
    : _b(new B) 
    { 
    } 
}; 

// dangling ptr (who deletes the instance?) 
class A 
{ 
    B *_b; 
    public: 
    A() 
    ... (same as above) 

    ~A() 
    { 
    delete _b; 
    } 
}; 

Pour résoudre ce problème, il existe plusieurs méthodes.

Toujours mettre en œuvre un constructeur de copie et opérateur = dans les classes qui utilisent des pointeurs de mémoire brutes.

class A 
{ 
    B *_b; 
    public: 
    A() 
    ... (same as above) 

    ~A() 
    ... 

    A(const A &rhs) 
    : _b(new B(rhs._b)) 
    { 
    } 

    A &operator=(const A &rhs) 
    { 
    B *b=new B(rhs._b); 
    delete _b; 
    _b=b; 
    return *this; 
}; 

Inutile de dire que cela est une douleur importante et il y a assez peu de subtilités pour obtenir le droit. Je ne suis même pas totalement sûr que je l'ai fait ici et je l'ai fait quelques fois. N'oubliez pas que vous devez copier tous les membres - si vous en ajoutez de nouveaux, n'oubliez pas de les ajouter aussi!

Faire le constructeur de copie et opérateur = privé dans votre classe. Ceci est la solution "verrouiller la porte". C'est simple et efficace, mais parfois surprotecteur.

class A : public boost::noncopyable 
{ 
    ... 
}; 

N'utilisez jamais de pointeurs bruts. C'est simple et efficace. Il y a beaucoup d'options:

  • Utilisez les classes de chaînes au lieu de pointeurs char premières
  • Utilisez std :: auto_ptr, boost :: shared_ptr, boost :: scoped_ptr etc

Exemple:

// uses shared_ptr - note that you don't need a copy constructor or op= - 
// shared_ptr uses reference counting so the _b instance is shared and only 
// deleted when the last reference is gone - admire the simplicity! 
// it is almost exactly the same as the "memory leak" version, but there is no leak 
class A 
{ 
    boost::shared_ptr<B> _b; 
    public: 
    A() 
    : _b(new B) 
    { 
    } 
}; 
+0

Votre opérateur d'affectation n'est pas exempt d'exception. Voir la question récente au sujet de la copie et de la sécurité d'exception: http://stackoverflow.com/questions/214891/checklist-for-writing-copy-constuctor-and-assignment-operator-in-c#214966 –

+0

Doh, ok je fixe . Vous voyez ce que je veux dire à propos de ce qui est difficile et difficile à obtenir alors oui –

+0

La meilleure façon de faire le constructeur et l'opérateur de copie = private est d'hériter de boost :: noncopyable. Vous devriez le faire pour chaque classe à moins que vous ne soyez certain que ce sera copiable. – CesarB

5

Oui, qui est une copie peu profonde. Vous avez maintenant deux copies de s (une dans l'appelant, une sur la pile en tant que paramètre), chacune contenant un pointeur vers ce même bloc de mémoire.

2

Vous aurez deux copies de la struct s, dont chacun aura son propre pointeur i, mais les deux i pointeurs auront la même valeur pointant vers la même adresse en mémoire - donc oui, ce sera une copie superficielle .

Questions connexes