2011-08-12 4 views
0

Vu le code suivant:C++ Copie Constructor Comportement étrange

class monomial { 
public: 
    mp_real coe; 
    int exp;   
    monomial *next; 
}; 

class polynomial 
{ 
private: 
    monomial *start; 
public: 
    polynomial(); 
    ~polynomial(); 
    void destroy_poly(monomial *); 
    polynomial & operator=(const polynomial &); 
    polynomial(const polynomial&); 
    monomial * get_start(); 
}; 
polynomial::polynomial() { 
    start = NULL; 
} 
polynomial::~polynomial() { 
    if (start != NULL) { 
     destroy_poly(start); 
    } 
    start = NULL; 
} 
void 
polynomial::destroy_poly(monomial * m) { 
    monomial * cm = m; 
    if (cm->next != NULL) { 
     destroy_poly(cm->next); 
    } 
    delete m; 
} 
polynomial::polynomial(const polynomial& p) { 
    if (p.start != NULL) { 
     start = new monomial(); 
     start->coe = p.start->coe; 
     start->exp = p.start->exp; 

     monomial * tmpPtr = p.start; 
     monomial * lastPtr = start; 

     while (tmpPtr->next != NULL) { 
      monomial * newMon = new monomial(); 
      newMon->coe = tmpPtr->next->coe; 
      newMon->exp = tmpPtr->next->exp; 

      lastPtr->next = newMon; 
      lastPtr = lastPtr->next; 
      tmpPtr = tmpPtr->next; 
     } 
    } 
} 
polynomial & polynomial::operator=(const polynomial &p) { 
    if (p.start != NULL) { 
     start = new monomial(); 
     start->coe = p.start->coe; 
     start->exp = p.start->exp; 

     monomial * tmpPtr = p.start; 
     monomial * lastPtr = start; 

     while (tmpPtr->next != NULL) { 
      monomial * newMon = new monomial(); 
      newMon->coe = tmpPtr->next->coe; 
      newMon->exp = tmpPtr->next->exp; 

      lastPtr->next = newMon; 
      lastPtr = lastPtr->next; 
      tmpPtr = tmpPtr->next; 
     } 
    } 
    return *this; 
} 

Puis, en main.cpp:

main() { 
    polynomial poly; 
    // code that initializes/sets values for 
    // various monomials pointed to by poly.start. 

    map<set<unsigned int>, polynomial> hash; 
    set<unsigned int> tmpSet; 

    tmpSet.insert(0); 

    hash[tmpSet] = poly; 
} 

je peux mettre un point d'arrêt sur la première ligne du constructeur de copie, et après que je step-over la ligne de hachage [tmpSet] = poly, p.start à l'intérieur du constructeur de copie est NULL. Ensuite, il est appelé une seconde fois, à quel moment p.start a des valeurs étranges à l'intérieur.

Qu'est-ce qui se passe?

Merci, Erich

EDIT 1: la pensée en ajoutant l'opérateur d'affectation à la classe polynomiale fixe, mais il n'a pas. Toujours avoir le même problème.

+1

Vous avez corrigé l'opérateur d'affectation, mais pas si p.start est null, ce qui est la valeur par défaut. Partout avec 'if (p.start!= NULL) {'devrait se terminer par'} else {start = NULL;} '(bien, n'oubliez pas de désallouer l'opérateur d'assignation en premier) –

+0

Eh bien peut-être que je me trompe ou quelque chose. S'il te plaît, ne m'appelle pas stupide, mais regarde. Vous mettez dans l'objet conteneur hash qui a des pointeurs à l'intérieur. Mettre l'objet dans le conteneur le fait copier (dans votre cas seulement l'adresse du pointeur). Lorsque le programme est hors de portée, il essaie de détruire le pointeur plusieurs fois. Donc là vous devriez utiliser un pointeur avec le nombre de références ou quelque chose. Corrigez si j'ai tort. – legion

+0

Merci. J'ai ajouté la déclaration else et cela fonctionne maintenant. Pourriez-vous expliquer un peu plus pourquoi cela a réglé le problème? En outre, que voulez-vous dire par "désaffecter dans l'opérateur d'affectation d'abord"? –

Répondre

4

Vous violez le Rule of Three:
Puisque vous surchargez Copier constructeur, vous Si surcharge opérateur d'affectation de copie et destructor pour votre classe.

La déclaration ci-dessus était en référence aux scénarios généraux, Dans votre exemple de code, vous pouvez remplacer le devrait avec un doit.

+0

"Doit" est peut-être trop fort. "Devrait" est plus approprié. –

+0

@Oli Charlesworth: Je suis d'accord, mais dans ce cas, le ** must ** est un must :) –

+0

@Als: Très vrai! Quoi qu'il en soit, +1. –

0

Vous devez ajouter un constructeur par défaut, où vous initialisez start to NULL.

+0

S'il vous plaît voir la modification ci-dessus. –

2

Deux numéros.

1) Vous n'avez pas de constructeur sans argument, donc le compilateur classe les valeurs initiales comme bon lui semble. Le fait que p.start est initialement NULL est

2) Lorsque votre constructeur de copie a adopté une polynomial avec p.start == NULL, vous n'initialise pas l'une des variables dans la classe, de sorte que la copie suivante pourrait avoir toute valeur initiale du compilateur Assigne (voir le problème n ° 1). Pour corriger, vous devez ajouter un constructeur sans argument qui initialise le polynôme dans un état sain. Et puis le constructeur de copie, s'il est passé tel un polynôme, devrait s'initialiser à un état sain.

+0

S'il vous plaît voir la modification ci-dessus. –

+0

Le deuxième point demeure. Si vous passez un polynôme vide au constructeur de copie, il voit que 'p.start' est NULL. Il ne définit ensuite jamais this-> start' sur NULL (le constructeur par défaut n'est pas exécuté pendant le constructeur de copie). –

0

Comme Als l'a souligné, vous violez la Règle des Trois. Eh bien regardez, quand vous mettez quelque chose dans le conteneur ce qui arrive à vos pointeurs.

monomial *start; 
monomial *next; 

Pensez-y.

+0

S'il vous plaît voir la modification ci-dessus. –

0

Votre constructeur de copie quitte l'ensemble à une valeur aléatoire si p.start == NULL. Cela pourrait être la cause du problème, selon l'autre code dans votre programme. De plus, vous n'avez aucun opérateur d'affectation, donc le compilateur fait des copies superficielles pour vous. Vous devez ajouter une autre fonction polynomial &operator=(const polynomial& b)

+0

S'il vous plaît voir la modification ci-dessus. –