2010-01-03 12 views
1

Je viens de recevoir une erreur seg en surchargeant l'opérateur d'assignation pour une classe FeatureRandomCounts, qui a _rects comme pointeur pointant vers un tableau de FeatureCount et une taille rhs._dim, et dont les autres membres de date sont non-pointeurs:erreur de segmentation dans l'opérateur de surcharge =

FeatureRandomCounts & FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs) 
{ 
    if (_rects) delete [] _rects; 

    *this = rhs; // segment fault 

    _rects = new FeatureCount [rhs._dim]; 
    for (int i = 0; i < rhs._dim; i++) 
    { 
    _rects[i]=rhs._rects[i]; 
    } 

    return *this;  
} 

Est-ce que quelqu'un a une idée? Merci et salutations!

Répondre

5

Comme mentionné, vous avez une récursivité infinie; Toutefois, pour ajouter à cela, voici un moyen infaillible pour mettre en œuvre op =:

struct T { 
    T(T const& other); 
    T& operator=(T copy) { 
    swap(*this, copy); 
    return *this; 
    } 
    friend void swap(T& a, T& b); 
}; 

Ecrivez une copie correcte cteur et d'échange, et la sécurité d'exception et tous les cas de bord sont traités pour vous!

Le paramètre copy est transmis par la valeur, puis modifié. Toutes les ressources que l'instance actuelle doit détruire sont gérées lorsque la copie est détruite. Cela suit current recommendations et gère self-assignment proprement.


#include <algorithm> 
#include <iostream> 

struct ConcreteExample { 
    int* p; 
    std::string s; 

    ConcreteExample(int n, char const* s) : p(new int(n)), s(s) {} 
    ConcreteExample(ConcreteExample const& other) 
    : p(new int(*other.p)), s(other.s) {} 
    ~ConcreteExample() { delete p; } 

    ConcreteExample& operator=(ConcreteExample copy) { 
    swap(*this, copy); 
    return *this; 
    } 

    friend void swap(ConcreteExample& a, ConcreteExample& b) { 
    using std::swap; 
    //using boost::swap; // if available 
    swap(a.p, b.p); // uses ADL (when p has a different type), the whole reason 
    swap(a.s, b.s); // this 'method' is not really a member (so it can be used 
        // the same way) 
    } 
}; 

int main() { 
    ConcreteExample a (3, "a"), b (5, "b"); 
    std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n'; 
    a = b; 
    std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n'; 
    return 0; 
} 

avis qu'il fonctionne soit avec les membres gérés manuellement (p) ou des membres de style SBRM RAII/(s).

+0

Pas tout à fait compris. La structure est-elle un modèle? Donc par échange, la copie sera l'original * ceci? – Tim

+0

Merci! Est-ce que le swap change aussi de "copie"? Est-ce plus qu'une mission? – Tim

14
*this = rhs; 

appelle opérateur =(), qui est la fonction que vous écrivez. Cue récursivité infinie, débordement de pile, crash. De plus, si vous utilisiez un vecteur std :: plutôt qu'un tableau de type C, vous n'auriez probablement pas du tout besoin d'implémenter operator =().

+0

Merci, Neil! Dans mon code, le tableau C est préféré à std :: vector. Mais c'est toujours une solution possible. – Tim

+5

Si vous préférez les tableaux par rapport aux vecteurs, vous vous trompez et vous faites énormément de travail inutile. –

+2

Le fait que vous ne puissiez pas écrire correctement l'opérateur d'affectation signifie que vous ne connaissez probablement pas tout un tas d'autres pièges associés à l'utilisation de tableaux alloués dynamiquement. Cela signifie que vous devriez certainement utiliser std :: vector <>. Préférer un tableau C n'est pas une raison mais plutôt un manque de compréhension de ce qui se passe réellement. –

3

La ligne suivante:

*this = rhs; // segment fault 

appellera votre fonction récursive operator=() entraînant un débordement de pile.

Vous devez probablement le remplacer par des affectations directes des différents champs membres.

As Neil said, en utilisant quelque chose comme std::vector<> enlèvera une grande partie de la responsabilité de votre code. Si, pour une raison quelconque, vous ne pouvez pas ou ne voulez pas utiliser std::vector<>, vous pouvez également envisager d'utiliser l'idiome d'échange pour votre opérateur d'affectation. Cela rendra l'exception de fonction sûre (si l'allocation de la mémoire pour la matrice FeatureCount échoue et déclenche une exception, l'objet d'origine affecté sera laissé inchangé). Quelque chose comme ce qui suit:

void FeatureRandomCounts::swap(FeatureRandomCounts& other) 
{ 
    FeatureCount* tmp_rects = other._rects; 
    int tmp_dim    = other._dim; // or whatever type _dim is 

    // similarly for other members of FeatureRandomCounts... 

    // now copy the other contents to 
    this->_rects = other._rects; 
    this->_dim = other._dim; 

    // assign other members of rhs to lhs 

    other._rects = tmp_rects; 
    other._dim = tmp_dim; 

    // etc. 

    return; 
} 

Maintenant votre mission peut ressembler à:

FeatureRandomCounts & FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs) 
{ 
    FeatureRandomCounts tmp(rhs); // make a copy 

    tmp.swap(*this);    // swap the contents of the copy and *this 

    return *this; 
            // the contents of tmp (which has the old 
            // stuff that was in *this) gets destructed 
} 

Notez que vous avez besoin d'un constructeur de copie approprié pour que cela fonctionne, mais étant donné le Big 3 rule dont vous avez besoin déjà cteur bonne copie .

+1

Dans votre implémentation de swap() vous pouvez au moins utiliser std :: swap() pour échanger les membres réels. –

+0

@Martin - vous avez raison, bien sûr. Je me suis tenu à l'écart de 'std :: anything' car l'OP indiquait quelque part qu'il ne pouvait/ne voulait pas utiliser' std :: vector'. J'aurais dû clarifier cela et mettre à jour la réponse plus tard pour indiquer que 'std :: swap' devrait être utilisé (en fait, il devrait s'agir d'un' swap() 'avec une bonne déclaration' using' donc le plus approprié 'swap()' est ramassé, avec 'std :: swap' étant le dernier recours). –

4
*this = rhs; // segment fault 

Ceci est définitivement pas la façon de le faire. Vous appelez = de manière récursive, sans appeler l'opérateur d'affectation intégré. Affectez les variables une par une. Ne sois pas paresseux.

+0

Merci. Je suis paresseux. Alors est-il possible d'appeler l'opérateur d'affectation par défaut? – Tim

+0

Non, une fois que vous aurez déclaré le vôtre, le compilateur n'en générera pas un (et ne pourrait pas, car il aurait la même signature). –