web-dev-qa-db-fra.com

Surcharge des opérateurs dans les structures typedef (c ++)

Je veux créer une structure typedef appelée pos (à partir de la position) qui stocke les coordonnées x et y. J'essaie de surcharger certains opérateurs pour cette structure, mais elle ne compile pas.

typedef struct {
    int x;
    int y;

    inline pos operator=(pos a) {
        x=a.x;
        y=a.y;
        return a;
    }

    inline pos operator+(pos a) {
        return {a.x+x,a.y+y};
    }

    inline bool operator==(pos a) {
       if (a.x==x && a.y== y)
          return true;
       else
          return false;
    }
} pos;

Je voulais aussi connaître la différence entre cela:

inline bool operator==(pos a) {
    if(a.x==x && a.y== y)
       return true;
      else
       return false;
}

Et ça:

bool operator==(pos a) const {
      if(a.x==x && a.y== y)
         return true;
      else
         return false;
}
25
tuket

La ventilation de votre déclaration et de ses membres est quelque peu jonchée:

Supprimer le typedef

Le typedef n'est ni requis, ni souhaité pour les déclarations de classe/struct en C++. Vos membres n'ont aucune connaissance de la déclaration de pos telle qu'écrite, qui est au cœur de votre échec de compilation actuel.

Change ça:

typedef struct {....} pos;

Pour ça:

struct pos { ... };

Supprimer les inserts superflus

Vous déclarez tous les deux et définissant vos opérateurs membres dans la définition de classe elle-même. Le mot clé inline n'est pas nécessaire tant que vos implémentations restent à leur emplacement actuel (la définition de classe)


Renvoyez les références à *this Le cas échéant

Ceci est lié à une abondance de constructions de copie au sein de votre implémentation qui ne devrait pas être effectuée sans une bonne raison de le faire. Il est lié à l'expression idéologique des éléments suivants:

a = b = c;

Cela affecte c à b, et la valeur résultante b est ensuite affectée à a. Ceci n'est pas équivalent au code suivant, contrairement à ce que vous pourriez penser:

a = c;
b = c;

Par conséquent, votre opérateur d'affectation doit être implémenté en tant que tel:

pos& operator =(const pos& a)
{
    x = a.x;
    y = a.y;
    return *this;
}

Même ici, cela n'est pas nécessaire. L'opérateur d'attribution de copie par défaut fera ce qui précède pour vous gratuitement (et code! Woot!)

Remarque: il y a des moments où ce qui précède devrait être évité en faveur du copier/échanger l'idiome . Bien que non nécessaire pour ce cas spécifique, il peut ressembler à ceci:

pos& operator=(pos a) // by-value param invokes class copy-ctor
{
    this->swap(a);
    return *this;
}

Ensuite, une méthode de swap est implémentée:

void pos::swap(pos& obj)
{
    // TODO: swap object guts with obj
}

Vous faites cela pour utiliser la classe copy-ctor pour faire une copie, puis utilisez un échange sans exception pour effectuer l'échange. Le résultat est que la copie entrante s'écarte (et détruit) les vieilles entrailles de votre objet, tandis que votre objet en assume la propriété. Lisez plus l'idiome de copie/échange ici , avec les avantages et les inconvénients.


Passez les objets par référence const le cas échéant

Tous vos paramètres d'entrée pour tous vos membres font actuellement des copies de tout ce qui est passé lors de l'invocation. Bien que cela puisse être trivial pour un code comme celui-ci, il peut être très cher pour les types d'objets plus grands. Un exemple est donné ici:

Change ça:

bool operator==(pos a) const{
    if(a.x==x && a.y== y)return true;
    else return false;
}

Pour cela: (également simplifié)

bool operator==(const pos& a) const
{
    return (x == a.x && y == a.y);
}

Aucune copie de n'importe quoi n'est effectuée, ce qui donne un code plus efficace.


Enfin, pour répondre à votre question, quelle est la différence entre une fonction membre ou un opérateur déclaré comme const et une fonction qui ne l'est pas?

Un membre const déclare qu'en invoquant ce membre, pas modifiera l'objet sous-jacent (les déclarations mutables ne résistent pas). Seules les fonctions membres const peuvent être appelées contre des objets const ou des références et des pointeurs const. Par exemple, votre operator +() ne modifie pas votre objet local et doit donc être déclarée comme const. Votre operator =() modifie clairement l'objet local, et donc l'opérateur ne doit pas être const.


Résumé

struct pos
{
    int x;
    int y;

    // default + parameterized constructor
    pos(int x=0, int y=0) 
        : x(x), y(y)
    {
    }

    // assignment operator modifies object, therefore non-const
    pos& operator=(const pos& a)
    {
        x=a.x;
        y=a.y;
        return *this;
    }

    // addop. doesn't modify object. therefore const.
    pos operator+(const pos& a) const
    {
        return pos(a.x+x, a.y+y);
    }

    // equality comparison. doesn't modify object. therefore const.
    bool operator==(const pos& a) const
    {
        return (x == a.x && y == a.y);
    }
};

[~ # ~] modifier [~ # ~] OP voulait voir comment fonctionne une chaîne d'opérateurs d'affectation. Ce qui suit montre comment cela:

a = b = c;

Est équivalent à ceci:

b = c;
a = b;

Et que cela ne correspond pas toujours à ceci:

a = c;
b = c;

Exemple de code :

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

struct obj
{
    std::string name;
    int value;

    obj(const std::string& name, int value)
        : name(name), value(value)
    {
    }

    obj& operator =(const obj& o)
    {
        cout << name << " = " << o.name << endl;
        value = (o.value+1); // note: our value is one more than the rhs.
        return *this;
    }    
};

int main(int argc, char *argv[])
{

    obj a("a", 1), b("b", 2), c("c", 3);

    a = b = c;
    cout << "a.value = " << a.value << endl;
    cout << "b.value = " << b.value << endl;
    cout << "c.value = " << c.value << endl;

    a = c;
    b = c;
    cout << "a.value = " << a.value << endl;
    cout << "b.value = " << b.value << endl;
    cout << "c.value = " << c.value << endl;

    return 0;
}

Sortie

b = c
a = b
a.value = 5
b.value = 4
c.value = 3
a = c
b = c
a.value = 4
b.value = 4
c.value = 3
66
WhozCraig

Au lieu de typedef struct { ... } pos; vous devriez faire struct pos { ... };. Le problème ici est que vous utilisez le nom de type pos avant qu'il ne soit défini. En déplaçant le nom en haut de la définition de la structure, vous pouvez utiliser ce nom dans la définition de la structure elle-même.

De plus, le typedef struct { ... } name; pattern est un C-isme, et n'a pas beaucoup de place en C++.

Pour répondre à votre question sur inline, il n'y a pas de différence dans ce cas. Lorsqu'une méthode est définie dans la définition de la structure/classe, elle est implicitement déclarée en ligne. Lorsque vous spécifiez explicitement inline, le compilateur l'ignore efficacement car la méthode est déjà déclarée en ligne.

(Les méthodes inline ne déclencheront pas d'erreur de l'éditeur de liens si la même méthode est définie dans plusieurs fichiers objets; l'éditeur de liens ignorera tout simplement tous sauf un, en supposant qu'ils sont tous de la même implémentation. C'est la seule garantie changement de comportement avec les méthodes en ligne. De nos jours, elles n'affectent pas la décision du compilateur quant à l'opportunité d'inclure ou non les fonctions; elles facilitent simplement la mise à disposition de la fonction dans toutes les unités de traduction, ce qui donne au compilateur option pour incorporer la fonction, s’il le juge utile.)

6
cdhowie

essaye ça:

struct Pos{
    int x;
    int y;

    inline Pos& operator=(const Pos& other){
        x=other.x;
        y=other.y;
        return *this;
    }

    inline Pos operator+(const Pos& other) const {
        Pos res {x+other.x,y+other.y};
        return res;
    }

    const inline bool operator==(const Pos& other) const {
        return (x==other.x and y == other.y);
    }
 };  
3
Krozark
  1. opérateur booléen == (pos a) const {- cette méthode ne change pas les éléments de l'objet.
  2. opérateur booléen == (pos a) {- il peut changer les éléments de l'objet.
2
maskotky