2009-10-04 9 views
2

Désolé pour la question de base, mais j'ai du mal à trouver la bonne chose à google.Est-ce que cette réaffectation C++ est valide?

#include <iostream> 
#include <string> 
using namespace std; 

class C { 
public: 
C(int n) { 
    x = new int(n); 
} 

~C() { 
    delete x; 
} 

int getX() {return *x;} 

private: 
    int* x; 
}; 

void main() { 
    C obj1 = C(3); 
    obj1 = C(4); 
    cout << obj1.getX() << endl; 
} 

On dirait qu'il fait correctement l'affectation, puis appelle la destructor sur obj1 laissant x avec une valeur de déchets plutôt que d'une valeur de 4. Si cela est valable, pourquoi fait-il cela?

+2

Pouvez-vous poster plus de code? est-ce que C a un constructeur de copie? que fait le déconstructeur? – LiraNuna

+1

Dupliquer de: http://stackoverflow.com/questions/255612/c-dynamically-allocating-an-array-of-objects/255744 –

+0

obj1 est laissé avec un pointeur invalide (qui a été supprimé lorsque C (4) se termine Laisser 2 problèmes (a) La mémoire allouée à l'origine est divulguée (b) La mémoire allouée par C (4) sera supprimée en double –

Répondre

2

S'il existe une classe C dont un constructeur prend un entier, ce code est-il valide?

C obj1(3); 
obj1=C(4); 

En supposant que C a une operator=(C) (ce qui sera par défaut), le code est valide. Ce qui va arriver c'est que dans la première ligne, obj1 est construit avec 3 comme paramètre pour le constructeur. Ensuite, sur la deuxième ligne, un objet C temporaire est construit avec 4 comme paramètre et ensuite operator= est appelé sur obj1 avec cet objet temporaire en tant que paramètre. Après cela, l'objet temporaire sera détruit. Si obj1 est dans un état non valide après l'affectation (mais pas avant), il y a probablement un problème avec operator= de C.

Mise à jour: Si x a vraiment besoin d'être un pointeur que vous avez trois options:

  1. permettent à l'utilisateur au lieu du destructor décider quand la valeur de x doit être supprimée en définissant une méthode de destruction que les besoins des utilisateurs appeler explicitement. Cela causera des fuites de mémoire si l'utilisateur oublie de le faire.
  2. Définissez operator= afin qu'il crée une copie de l'entier au lieu d'une copie de la valeur. Si dans votre code réel vous utilisez un pointeur vers quelque chose de beaucoup plus grand qu'un int, cela pourrait être trop cher.
  3. utiliser le comptage de référence pour garder une trace nombre d'instances de C détiennent un pointeur sur le même objet et supprimer l'objet lorsque son compteur atteint 0.
+0

Je pense que vous êtes probablement droite. J'ai probablement besoin d'implémenter operator = (C) correctement pour la mémoire dynamique que j'ai en cours. –

+2

C'est faux le code est cassé. –

+1

Voir ici pour un exemple de pourquoi il est cassé: http://stackoverflow.com/questions/255612/c-dynamically-allocation-an-array-of-objects/255744#255744 –

0

Lorsque vous testez la valeur de obj1? Est-ce après avoir quitté la portée?

Dans votre exemple, obj1 est un objet de pile. Cela signifie que dès que vous quittez la fonction dans laquelle il a été défini, il est nettoyé (le destructeur est appelé). Essayez allouer l'objet sur le tas:

C *obj1 = new C(3); 
delete obj1; 
obj1 = new C(4);
1

NOUVEAU: Ce qui se passe est que votre destructor a deallocated la mémoire allouée par le constructeur de C (4). Ainsi, le pointeur que vous avez copié au-dessus de C (4) est un pointeur ballants-à-dire qu'il pointe encore à l'emplacement de mémoire de la mémoire désallouée

class C { 
public: 
C(int n) { 
    x = new int(n); 
} 

~C() { 
    //delete x; //Don't deallocate 
} 

void DeallocateX() 
{ 
delete x; 
} 

int getX() {return *x;} 

private: 
    int* x; 
}; 

int main(int argc, char* argv[]) 
{ 
    // Init with C(3) 
    C obj1 = C(3); 
    // Deallocate C(3) 
    obj1.DeallocateX(); 
    // Allocate memory and store 4 with C(4) and pass the pointer over to obj1 
    obj1 = C(4); 
    // Use the value 
    cout << obj1.getX() << endl; 
    // Cleanup 
obj1.DeallocateX(); 


return 0; 
} 
+0

Pourquoi libère-t-il la mémoire allouée par le constructeur de C (4)? Ne devrait-il pas libérer la mémoire allouée par C (3), alors utiliser le constructeur pour C (4)? –

+0

Ouais vous avez raison, il utilise d'abord le destructeur de C (3), puis le constructeur de C (4) et ensuite le destructeur de C (4) car il est hors de portée.Donc maintenant, obj1 :: x pointe vers l'adresse de la mémoire allouée par C (4) mais il a été désalloué par son destructeur, donc il pointe vers la poubelle. Par conséquent, ma suggestion de libérer la mémoire manuellement. Avez-vous essayé d'exécuter le code? Je suis sûr que cela fonctionne :) – Jacob

+0

Donc, pour répondre à votre question, oui, il libère C (3), puis utilise C (4), mais le code original avec le destructeur désalloue C (4), donc maintenant x pointe sur des ordures. – Jacob

2

Si C contient un pointeur vers quelque chose, vous avez besoin à peu près toujours à mettre en œuvre opérateur =. Dans votre cas, il aurait cette signature

class C 
{  
    public: 
    void operator=(const C& rhs) 
    { 
     // For each member in rhs, copy it to ourselves 
    } 
    // Your other member variables and methods go here... 
}; 
+1

Vous devez également implémenter le constructeur de copie. Implémenter l'opérateur d'affectation en termes de constructeur de copie est relativement trivial. –

2

Je ne connais pas suffisamment le C++ subtil pour expliquer le problème que vous rencontrez. Je sais, cependant, qu'il est beaucoup plus facile de s'assurer qu'un cours se comporte comme vous le souhaitez si vous suivez le Rule of Three, que le code que vous avez publié viole.En fait, il est dit que si vous définissez l'un des vous suivant devez définir les trois:

  • Destructeur
  • Copie constructeur
  • Opérateur d'affectation

Notez également que l'opérateur d'affectation des besoins de mise en œuvre gérer correctement le cas où un objet est assigné à lui-même (soi-disant "auto-affectation"). Ce qui suit devrait fonctionner correctement (non testé):

#include <iostream> 
#include <string> 
using namespace std; 

class C { 
public: 
C(int n) { 
    x = new int(n); 
} 

C(const C &other): C(other.getX()) { } 

~C() { 
    delete x; 
} 

void operator=(const C &other) { 
    // Just assign over x. You could reallocate if you first test 
    // that x != other.x (the pointers, not contents). The test is 
    // needed to make sure the code is self-assignment-safe. 
    *x = *(other.x); 
} 

int getX() {return *x;} 

private: 
    int* x; 
}; 

void main() { 
    C obj1 = C(3); 
    obj1 = C(4); 
    cout << obj1.getX() << endl; 
} 
+0

Merci, c'est aussi juste ce que j'avais besoin de savoir, bien que je devais changer le constructeur de copie pour avoir un corps de * x = other.getX(); –

+0

Ainsi, si le constructeur de copie et l'opérateur d'affectation font essentiellement la même chose, y a-t-il un moyen de réutiliser l'un pour implémenter l'autre? –

+1

Votre implémentation n'est pas sûre pour la mémoire. Comme operator = ne fait pas de copie en profondeur, le pointeur sur x est supprimé par le destructeur de l'objet temporaire. I.e C a (4); C b (5); a = b; - l'original a.x est divulgué, et b.x est supprimé par a et b quand ils sont hors de portée. –

1

Soyez explicite au sujet de la propriété des pointeurs! auto_ptr est génial pour ça. En outre, lors de la création d'un local, ne faites pas C obj1 = C(3) qui crée deux instances de C et initialise le premier avec le constructeur de copie de la seconde.

Heed The Guru.

class C { 
public: 
    C(int n) : x(new int(n)) { } 
    int getX(){ return *x; } 
    C(const C& other) : x(new int(*other.x)){} 
    C& operator=(const C& other) { *x = *other.x; return *this; } 
private: 
    std::auto_ptr<int> x; 
}; 

int main() { 
    C obj1(3); 
    obj1 = C(4); 
    std::cout << obj1.getX() << std::endl; 
} 
+0

Si proche mais pas de biscuit. Votre opérateur d'affectation vous laisse avec deux pointeurs auto tenant le même pointeur. Vous obtiendrez une double suppression. –

+1

Les opérateurs de déréférencement sur les lhs et rhs auto_ptr provoquent une copie de la valeur de l'adresse de la rhs, à l'adresse sur les lhs, pas une copie des adresses elles-mêmes. – joshperry

1

Fondamentalement, vous essayez de redéfinir un pointeur intelligent.
Ce n'est pas trivial pour être correct pour toutes les situations.

Veuillez d'abord regarder les pointeurs intelligents disponibles dans la norme.

Une implémentation de base (qui échouera dans certaines situations (copiez l'une des normes pour en obtenir une meilleure)). Mais cela devrait couvrir les bases:

class X 
{ 
    int*  data; 

    public: 
     // Destructor obvious 
     ~X() 
     { 
      delete data; 
     } 
     // Easy constructor. 
     X(int x) 
      :data(new int(x)) 
     {} 
     // Copy constructor. 
     // Relatively obvious just do the same as the normal construcor. 
     // Get the value from the rhs (copy). Note A class is a friend of 
     // itself and thus you can access the private members of copy without 
     // having to use any accessor functions like getX() 
     X(X const& copy) 
      :data(new int(copy.x)) 
     {} 
     // Assignment operator 
     // This is an example of the copy and swap idiom. This is probably overkill 
     // for this trivial example but provided here to show how it is used. 
     X& operator=(X const& copy) 
     { 
      X tmp(copy); 
      this->swap(tmp); 
      return this; 
     } 
     // Write a swap() operator. 
     // Mark it is as no-throw. 
     void swap(X& rhs) throws() 
     { 
      std::swap(data,rhs.data); 
     } 

    }; 
Questions connexes