2009-11-17 8 views
0

opérateur complexe * (double M const Complexe & c) {return c * m; }une constante à un problème complexe et surcharge opérateur

* Sur le code ci-dessus, j'essaie de multiplier une constante par un nombre complexe. je reçois des erreurs un d'entre eux est [ 'opérateur ' binaire a trop de paramètres]

ostream & opérateur < < (ostream & os, Complexe & c) {os < < C.Real < < "," < < c.imaginaire; return os;}

***** Pouvez-vous me dire ce que j'ai fait de mal sur cette ligne aussi bien. Merci *****

 #include <iostream> 
     using namespace std; 
     class Complex 
     { 
     private: 
      double real; 
      double imaginary; 
     public: 
      Complex(); 
      Complex(double r, double i = 0); 
      Complex operator*(const Complex & c) const; 
      Complex operator*(double mult) const; 
      Complex operator*(double m, const Complex & c) 
        { return c * m; }  
      ostream & operator<<(ostream & os, Complex & c) 
      {os << c.real <<"," << c.imaginary; return os;} 

     }; 
     Complex::Complex() 
     { 
      real = imaginary = 0; 
     } 

     Complex::Complex(double r, double i) 
     { 
      real = r; 
      imaginary = i; 
     } 
     Complex Complex::operator*(const Complex & c) const 

     { 
      Complex mult; 
      mult.imaginary = imaginary * c.imaginary; 
      mult.real = real * c.real; 
      return mult; 
     } 

     Complex Complex::operator*(double mult) const 
     { 
      Complex result; 
      result.real = real * mult; 
      result.imaginary = imaginary * mult; 
      return result; 
     } 
     int main() 
     { 
      Complex B(5, 40); 
      Complex C(6, 15); 
      cout << "B, and C:\n"; 
      cout << B << ": " << C << endl; 
      cout << "B * C: " << B*C << endl; 
      cout << "10 * B: " << 10*B << endl; 
      return 0; 
     } 
+1

La réponse de l'OSMŒ est correcte, je voulais juste faire des commentaires sur votre multiplication complexe. '(a + bi) * (c + di)' n'est pas égal '(ac + bdi)', mais '((ac-bd) + (ad + bc) i)' –

Répondre

3

Ces deux opérateurs ont des problèmes:

class Complex { 
    // ... 
    Complex operator*(double m, const Complex & c) 
    {return c * m;} 

    ostream & operator<<(ostream & os, Complex & c) 
    {os << c.real <<"," << c.imaginary; return os;} 
    // ... 
}; 

Avant toute chose: Ce operator<< n'est pas censé changer les sorties complexes, ce qui devrait être const. (Sinon, vous ne pouvez pas générer d'objets temporaires, car ils ne peuvent pas être liés à des références non const.)

Comme ce sont des fonctions membres non statiques, ils ont un paramètre this implicite. Avec cela, ils ont trois paramètres. Cependant, les deux sont des opérateurs binaires. Puisque vous ne pouvez pas les rendre statiques (c'est juste parce que les règles le disent), vous devez les implémenter en tant que fonctions libres.Cependant, comme ils sont mis en œuvre, ils ont besoin d'un accès aux membres privés, donc vous devez les faire des amis de votre classe:

class Complex { 
    // ... 
    friend Complex operator*(double m, const Complex & c); 
    friend ostream & operator<<(ostream & os, const Complex & c); 
    // ... 
}; 

Complex operator*(double m, const Complex & c) 
{return c * m;} 

ostream & operator<<(ostream & os, const Complex & c) 
{os << c.real <<"," << c.imaginary; return os;} 

Sur sidenote, il est possible de les mettre en œuvre en ligne au moment de la déclaration d'ami , ce qui vous ramène presque à votre version originale:

// note the friend 
class Complex { 
    // ... 
    friend Complex operator*(double m, const Complex & c) 
    {return c * m;} 

    friend ostream & operator<<(ostream & os, const Complex & c) 
    {os << c.real <<"," << c.imaginary; return os;} 
    // ... 
}; 

Cependant, si vous implémentez un type mathématique concrète, les utilisateurs de votre type s'attendront à toutes les opérations communes pour ces types de travailler avec elle. Autrement dit, ils s'attendent, par exemple, à ce que c*=r fonctionne simplement. Donc, vous aurez besoin de surcharger operator*=, aussi. Mais cet opérateur fait presque la même chose que operator*, donc ce serait une bonne idée d'implémenter l'un sur l'autre. Un idiome commun consiste à implémenter *= (et += etc.) en tant que fonctions membres (puisqu'elles changent leur argument gauche, c'est une bonne idée pour elles d'avoir accès à ses données privées) et operator* en tant que non-membre en plus de cela. (Habituellement, c'est plus efficace que l'inverse.):

// note the friend 
class Complex { 
    // ... 
    Complex& operator*=(double rhs) 
    {return /* whatever */;} 

    friend ostream & operator<<(ostream & os, const Complex & c) 
    {os << c.real <<"," << c.imaginary; return os;} 
    // ... 
}; 

inline Complex operator*(Complex lhs, double rhs) // note: lhs passed per copy 
{return lhs*=rhs;} 

inline Complex operator*(double lhs, const Complex& rhs) 
{return rhs*lhs;} 

IMO c'est la meilleure solution.


J'ai. Cependant, un peu plus de choses à dire:

La façon dont vous avez implémenté votre multiplication est inefficace:

Complex Complex::operator*(const Complex & c) const 
{ 
    Complex mult; 
    mult.imaginary = imaginary * c.imaginary; 
    mult.real = real * c.real; 
    return mult; 
} 

Quand vous dites Complex mult;, vous appelez le constructeur par défaut de votre classe, initialisant les parties réelles et imaginaires à 0. La prochaine chose que vous faites est d'écraser cette valeur. Pourquoi ne pas le faire en une seule étape:

Complex Complex::operator*(const Complex & c) const 
{ 
    Complex mult(real * c.real, imaginary * c.imaginary); 
    return mult; 
} 

ou même plus concis

Complex Complex::operator*(const Complex & c) const 
{ 
    return Complex(real * c.real, imaginary * c.imaginary); 
} 

Bien sûr, il est juste deux missions par multiplication. Mais alors - vous ne voudriez pas avoir cela dans une boucle interne de votre pilote graphique.

En outre, vos constructeurs ne sont pas implémentés comme il devrait être (TM). Pour l'initialisation des données membres, vous devez utiliser la liste initialiseur:

Complex::Complex() 
    : real(), imaginary() 
{ 
} 

Complex::Complex(double r, double i) 
    : real(r), imaginary(i) 
{ 
} 

Alors qu'il ne fait aucune différence pour les types intégrés comme double, il ne fait pas mal non plus et il est bon de ne pas entrer dans la habitude. Avec les types définis par l'utilisateur (un nom un peu malheureux, puisque c'est pour tous les non-intégrés, même des types comme std::string, qui n'est pas défini par les utilisateurs) qui ont un constructeur par défaut non trivial, cela fait la différence: c'est beaucoup moins efficace.

La raison est que, lorsque l'exécution passe ce { initial, C++ garantit que vos objets de membre de données sont accessibles et utilisables. Pour cela, ils doivent être construits, parce que la construction est ce qui transforme la mémoire brute en objets. Ainsi, même si vous n'appelez pas explicitement un constructeur, le système d'exécution appellera toujours les constructeurs par défaut. Si la prochaine chose que vous faites est de remplacer les valeurs construites par défaut, vous perdez à nouveau des cycles CPU.

Enfin, ce constructeur Complex(double r, double i = 0) sert d'opérateur de conversion implicite.Si, par exemple, vous vouliez appeler f(real), mais avez oublié de l'inclure, mais qu'il existe un f(const complex&) dans la portée, le compilateur exerce son droit d'effectuer une conversion définie par l'utilisateur et votre appel f(4.2) devient f(Complex(4.2)) et la mauvaise fonction est appelé silencieusement. C'est très dangereux.

Pour éviter cela, vous devez marquer tous les constructeurs explicit que l'on pourrait appeler avec un seul argument:

class Complex { 
    // ... 
    explicit Complex(double r, double i = 0) 
    // ... 
}; 
+0

Votre message est excellent, mais je Je suggère d'utiliser un seul 'opérateur *' en dehors de la classe pour multiplier deux valeurs 'Complex', et de laisser la conversion implicite s'occuper du cas où il y a un' double' d'un côté. – coppro

+0

@coppro: Ceci est possible, mais moins efficace, car il construirait inutilement un objet. – sbi

+0

Et probablement pire encore que de construire inutilement un objet, il ferait inutilement deux multiplications (par une partie imaginaire nulle) et addition/soustraction (des résultats). –

2

Ne pas déclarer Complex operator*(double m, const Complex & c) et ostream un comme fonctions membres (aka méthodes): les déclarer comme ami fonctions à la place! (Si vous avez même besoin d'eux pour être amis, ce qui ne serait pas le cas si vous aviez les méthodes d'accès en ligne évidentes pour les parties imaginaires et réelles - mais de toute façon le fait est qu'ils doivent être en dehors de la classe!).

0

Pour les fonctions membres, operator* et operator<< prendre un seul paramètre, comme l'objet Complex est déjà implicitement donné, à savoir

c * r 

se traduit

c.operator*(r) 

Si vous souhaitez avoir les deux forme de l'argument, puis une fonction d'ami externe est ce que vous cherchez. Bien que, comme le souligne Alex, si vous avez re et im configurés, alors vos opérateurs externes n'ont pas besoin d'être amis.

Questions connexes