User Tools

Site Tools


in204:tds:sujets:td5:part1

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
in204:tds:sujets:td5:part1 [2019/10/22 17:55]
bmonsuez [Question n°1.1]
in204:tds:sujets:td5:part1 [2022/11/18 10:48] (current)
Line 30: Line 30:
  
 <hidden Correction>​ <hidden Correction>​
 +<nodisp 2>
 Pour convertir un nombre en virgule flottante en un nombre complexe, il faut définir de [[in204:​cpp:​syntax:​class:​constructor|nouveaux constructeurs]] qui vont prendre un seul argument qui aura pour type le type d'un nombre à virgule flottante. ​ Pour convertir un nombre en virgule flottante en un nombre complexe, il faut définir de [[in204:​cpp:​syntax:​class:​constructor|nouveaux constructeurs]] qui vont prendre un seul argument qui aura pour type le type d'un nombre à virgule flottante. ​
  
Line 63: Line 64:
 }; };
 </​code>​ </​code>​
- 
 </​hidden>​ </​hidden>​
 ==== Question n°1.2===== ==== Question n°1.2=====
Line 69: Line 69:
 Ajouter à cette classe une fonction qui affiche le nombre complexe sur la console. Ajouter à cette classe une fonction qui affiche le nombre complexe sur la console.
  
 +<hidden Correction>​
 +Nous pouvons ajouter une fonction simple qui affiche le résultat sur la console. Cette fonction affiche la représentation minimale du nombre complexe.
 +
 +<code cpp>
 +class Complex
 +{
 +private:
 +...
 +public:
 +    void print() const
 +    {
 +        if(mRealPart != 0.0)
 +        {
 +            std:cout << mRealPart;
 +            if(mImaginaryPart != 0.0)
 +                std::cout << " + " << mImaginaryPart << " I";
 +        }
 +        else if(mImaginaryPart != 0.0)
 +            std::cout << " + " << mImaginaryPart << " I";
 +        else
 +            std::cout << "​0.0"; ​      
 +    }
 +...
 +};    ​
 +</​code>​
 +</​hidden>​
 ==== Question n°1.3===== ==== Question n°1.3=====
  
 Créer deux fonctions statiques créant des nombre complexes, l’une créant un nombre complexe à partir d’une paire correspondant à la partie réelle et imaginaire, l’autre créant un nombre complexe à partir de la paire (ρ,θ) correspondant à la notation polaire de ce nombre. Créer deux fonctions statiques créant des nombre complexes, l’une créant un nombre complexe à partir d’une paire correspondant à la partie réelle et imaginaire, l’autre créant un nombre complexe à partir de la paire (ρ,θ) correspondant à la notation polaire de ce nombre.
  
 +<hidden Correction>​
 +La difficulté,​ c'est qu'un nombre complexe admet deux représentations canoniques, une représentation sous la forme d'une partie réelle et une partie imaginaire ou au contraire une représentation sous la forme de coordonnées polaire.
 +
 +Si nous souhaitons définir un constructeur pour construire un nombre complexe à partir de ses parties réelles et imaginaires,​ nous allons nous retrouver avec un constructeur ayant comme signature, la signature suivante :
 +
 +<code cpp>
 +    Complex(double theRealPart,​ double theImaginaryPart); ​   ​
 +</​code>​
 +
 +Le constructeur qui construit un nombre complex à partie des coordonnées polaires aura la signature suivante :
 +
 +<code cpp>
 +    Complex(double rho, double theta); ​   ​
 +</​code>​
 +
 +c'est à dire que les deux constructeurs auront la même signature ''​Complex(double,​ double)''​ et le compilateur se retrouvera avec deux définitions différentes du même constructeur,​ d'où une erreur au moment de la compilation.
 +
 +Alors il existe plusieurs méthodes pour résoudre ce problème. Soit nous définissons des méthodes statiques, par exemple ''​from_real_imaginary''​ et ''​from_polar''​. Dans ce cas, nous devons définir le constructeur '' ​   Complex(double theRealPart,​ double theImaginaryPart)''​ comme étant un constructeur privé.
 +
 +<code cpp>
 +class Complex
 +{
 +private:
 +...
 +    Complex(double theRealPart,​ double theImaginaryPart): ​
 +        mRealPart(theRealPart),​ mImaginaryPart(theImaginaryPart) {}    ​
 +...
 +public:
 +...
 +
 +    static Complex from_real_imaginary(double theRealPart,​ double theImaginaryPart)
 +    {
 +        return Complex(theRealPart,​ theImaginaryPart);​
 +    }
 +    static Complex from_polar(double theRho, double theTheta)
 +    {
 +        return Complex(
 +           ​theRho* cos(theTheta), ​
 +           ​theRho* sin(theTheta));​
 +    }
 +};
 +</​code>​
 +
 +Pour créer un nombre complexe à partir de ses parties réelles et imaginaires,​ resp. ses coordonnées polaires, il faudra l'une des deux méthodes statiques :
 +<code cpp>
 +    Complex complexA Complex::​from_real_and_imaginary(1.0,​ 2.0);
 +    Complex complexB = Complex::​from_polar(1.0,​ 0,52);
 +</​code>​
 +
 +
 +Une autre approche consiste à ajouter des paramètres pour lever l'​ambiguité au moment de la compilation.
 +
 +<code cpp>
 +class Complex
 +
 +public:
 +...
 +   class Polar {}; // Classe vide ne servant qu'à définir un 
 +                   // type additionnel pour identifier ​
 +                   // une conversion polaire vers complexe.
 +private:
 +...
 +
 +public:
 +...
 +    Complex(double theReal, double theImaginary)
 +        re(theReal),​ im(theImaginary) ​  // Constructeur construisant un nombre complexe
 +        {}                              // à partir de sa partie réelle et imaginaire.
 +    Complex(Polar,​ double theRho, double thePhi)
 +        re(theRho* cos(thePhi)),​ im(theRho* sin(thePhi)) // Constructeur construisant un nombre complexe
 +        {}                              // à partir de ses coordonnées polaires.
 +...
 +};
 +</​code>​
 +
 +Désormais, les deux constructeurs ont bien une signature de type différente :
 +
 +<code cpp>
 +    Complex(double,​ double)
 +    Complex(Polar,​ double, double)
 +</​code>​
 +
 +Pour créer un nombre complexe à partir de ses parties réelles et imaginaires,​ resp. ses coordonnées polaires, il suffira d'​écrire :
 +<code cpp>
 +    Complex complexA(1.0,​ 2.0);
 +    Complex complexB(Polar(),​ 1.0, 0,52);
 +</​code>​
 +
 +</​hidden>​
 ==== Question n°1.4 ===== ==== Question n°1.4 =====
  
 Créer une variable globale ayant comme type la classe complexe et ayant pour valeur la valeur imaginaire 1. Créer une variable globale ayant comme type la classe complexe et ayant pour valeur la valeur imaginaire 1.
 +
 +<hidden Correction>​
 +Pour ce faire, il suffit de définir une variable globale :
 +
 +<code cpp>
 +const Complex I(0.0, 1.0);
 +</​code>​
 +
 +ou sinon :
 +
 +<code cpp>
 +const Complex I = Complex::​from_real_imaginary(0.0,​ 1.0);
 +</​code>​
 +
 +en fonction de l'​implantation que vous avez effectué aux questions précédentes.
 +
 +Cependant, la difficulté,​ c'est qu'une variable globale ne peut être définie que dans une seule et unique unité de compilation. Dans ce cas, il suffit d'​écrire dans le fichier ''​complex.cpp''​
 +
 +<code cpp>
 +// Fichier Complex.cpp
 +#​include"​Complex.hpp"​
 +
 +const Complex I(0.0, 1.0);
 +
 +</​code>​
 +
 +Cependant, dans ce cas, la variable n'est que visible dans l'​unité de compilation ''​Complex.cpp''​. Pour la rendre visible dans les autres unités de compilation,​ il est nécessaire de déclarer son existence dans le fichier ''​Complex.hpp''​ pour que les unités de compilation faisant référence aux nombres complexes puissent faire référence à la variable ''​I''​.
 +
 +Nous devons donc ajouter la déclaration suivante :
 +
 +<code cpp>
 +#ifndef ComplexHPP
 +#define ComplexHPP
 +...
 +
 +class Complex ​
 +{
 +...
 +};
 +...
 +extern const Complex I;
 +...
 +#endif
 +</​code>​
 +
 +Une approche permettant de simplifier l'​écriture est de définir un identificateur permettant de savoir si le fichier ''​Complex.hpp''​ est chargé par le fichier ''​Complex.cpp'',​ ie. si c'est l'​unité ''​Complex.cpp''​ qui est en cours de compilation. Dans ce cas, il est possible d'​effectuer l'​ensemble de la déclaration dans le fichier ''​Complex.h'',​ le fichier ''​Complex.cpp''​ se contentant de définir un symbole comme par exemple ''​complexCPP''​
 +
 +Le fichier ''​Complex.hpp''​ a désormais la structure suivante :
 +<code cpp>
 +#ifndef ComplexHPP
 +#define ComplexHPP
 +...
 +
 +class Complex ​
 +{
 +...
 +};
 +...
 +#ifndef complexCPP
 +extern const Complex I;
 +#else
 +const Complex I(0.0, 1.0);
 +#endif
 +...
 +#endif
 +</​code>​
 +
 +et le fichier ''​Complex.cpp''​ :
 +
 +<code cpp>
 +// Fichier Complex.cpp
 +#define complexHPP
 +#​include"​Complex.hpp"​
 +
 +</​code>​
 +</​hidden>​
  
 ==== Question n°1.5===== ==== Question n°1.5=====
Line 84: Line 275:
     Complex complexValue = 3.3 + 5 * I;     Complex complexValue = 3.3 + 5 * I;
 </​code>​ </​code>​
 +
 +<hidden Correction>​
 +En fait, il est nécessaire de déclarer un opérateur de multiplication par une valeur numérique avant de pouvoir procéder à cette écriture.
 +
 +Pour ce faire vous ajouter la déclaration suivante :
 +
 +<code cpp>
 +
 +class Complex
 +{
 +    friend Complex operator * (int, const Complex&​);​
 +    friend Complex operator * (double, const Complex&​);​
 +    friend Complex operator * (const Complex&,​ int);
 +    friend Complex operator * (const Complex&,​ double);
 +private:
 +...
 +};
 +
 +inline Complex operator * (int theLeftValue,​ const Complex&​ theRightValue)
 +{
 +    return Complex(theLeftValue * theRightValue.mRealPart, ​
 +        theRightValue.mImaginaryPart);​
 +}
 +inline Complex operator * (const Complex&​ theLeftValue,​ int theRightValue)
 +{
 +    return Complex(theLeftValue.mRealPart * theRightValue, ​
 +        theLeftValue.mImaginaryPart);​
 +}
 +inline Complex operator * (double theLeftValue,​ const Complex&​ theRightValue)
 +{
 +    return Complex(theLeftValue * theRightValue.mRealPart, ​
 +        theRightValue.mImaginaryPart);​
 +}
 +inline Complex operator * (const Complex&​ theLeftValue,​ double theRightValue)
 +{
 +    return Complex(theLeftValue.mRealPart * theRightValue, ​
 +        theLeftValue.mImaginaryPart);​
 +}
 +</​code>​
 +
 +</​hidden>​
  
 ===== Question n°2===== ===== Question n°2=====
Line 98: Line 330:
 </​code>​ </​code>​
  
 +<hidden Correction>​
 +
 +<code cpp>
 + Complex operator + (const Complex&​ aRightValue) const
 +        {
 +            Complex result(this);​
 +            result.mRealPart += aRightValue.mRealPart;​
 +            result.mImaginaryPart += aRightValue.mImaginaryPart;​
 +            return result;
 +        }
 + Complex operator - (const Complex&​ aRightValue) const;
 +        {
 +            Complex result(this);​
 +            result.mRealPart -= aRightValue.mRealPart;​
 +            result.mImaginaryPart -= aRightValue.mImaginaryPart;​
 +            return result;
 +        }
 +</​code>​
 +</​hidden>​
 ==== Question n°2.2==== ==== Question n°2.2====
  
Line 108: Line 359:
  
 Expliquer la différence avec les opérations précédentes ? Expliquer la différence avec les opérations précédentes ?
 +
 +<hidden Correction>​
 +<code cpp>
 +class Complex
 +{
 +    ...
 +    public:
 + Complex operator + (double aRightValue) const
 +        {
 +            Complex result(this);​
 +            result.mRealPart += aRightValue;​
 +            return result;
 +        }
 + Complex operator - (double aRightValue) const;
 +        {
 +            Complex result(this);​
 +            result.mRealPart -= aRightValue;​
 +            return result;
 +        }
 +</​code>​
 +
 +Ces fonctions calculent l'​addition d'un nombre à virgule flottante avec un nombre complexe, cependant, le nombre complexe est à gauche, le nombre à virgule flottante est à droite. Si nous voulons avoir un nombre à virgule flottante à gauche et un nombre complex à droite, il n'est pas possible de définir l'​opérateur dans la classe mais en dehors de la classe. Les opérateurs définis dans la classe suppose que le premier argument de l'​opéateur est l'​objet lui-même.
 +
 +<code cpp>
 +class Complex
 +{
 +    friend Complex operator +(double, const Complex&​);​
 +    friend Complex operator -(double, const Complex&​);​
 +    ...
 +    public:
 + Complex operator + (double aRightValue) const
 +        {
 +            Complex result(this);​
 +            result.mRealPart += aRightValue;​
 +            return result;
 +        }
 + Complex operator - (double aRightValue) const;
 +        {
 +            Complex result(this);​
 +            result.mRealPart -= aRightValue;​
 +            return result;
 +        }
 +   ...
 +};
 +
 +Complex operator +(double aLeftValue, const Complex&​ aRightValue)
 +{
 +    Complex result(aRightValue);​
 +    result.mRealPart += aRightValue;​
 +    return result;
 +}
 +Complex operator -(double aLeftValue, const Complex&​ aRightValue)
 +{
 +    Complex leftValue(aLeftValue,​ 0.0);
 +    return leftValue - aRightValue;​
 +}
 +
 +</​code>​
 +
 +</​hidden>​
 +
  
 ==== Question n°2.3 ====  ==== Question n°2.3 ==== 
Line 115: Line 427:
  Complex complexValue = 3.3 + 5 * I;  Complex complexValue = 3.3 + 5 * I;
 </​code>​ </​code>​
 +
 +<hidden Correction>​
 +Nous pouvons désormais définir des types complexes et effectué des calculs sur ces types complexes.
 +
 +Ainsi il sera possible de définir :
 +
 +<code cpp>
 +Complex I(0, 1);
 +
 +Complex complexValue = 3.3 + I;
 +
 +</​code>​
 +
 +Cependant, le code suivant est ambiguë :
 +
 +<code cpp>
 + Complex complexValue = 3.3 + 5 * I;
 +</​code>​
 +En effet, il faudrait être mesure de faire une multiplication entre un nombre entier ou un nombre à virgule flottante et un nombre complexe. Nous verrons comment faire pour effectuer ce type conversion à la fin de l'​exercice.
 +
 +</​hidden>​
 +
  
 ==== Question n°2.4 ====  ==== Question n°2.4 ==== 
Line 134: Line 468:
  
 ==== Question n°3.2 ====  ==== Question n°3.2 ==== 
-Proposer une surcharge des opérations ​et . Implanter ces dernières et tester.+Proposer une surcharge des opérations ​et /. Implanter ces dernières et tester.
  
 <code cpp> <code cpp>
Line 141: Line 475:
 </​code>​ </​code>​
    
 +<hidden Correction>​
 +<code cpp>
 + Complex operator * (const Complex&​ aRightValue) const
 +        {
 +            return Complex(
 +                mRealPart * aRightValue.mRealPart - mImaginaryPart * aRighValue.mImaginaryPart,​
 +                mRealPart * aRightValue.mImaginaryPart + mImaginaryPart * aRightValue.mRealPart);​
 +        }
 + Complex operator / (const Complex&​ aRightValue) const
 +        {
 +            double squareNorm = aRightValue.mRealPart * aRightValue.mRealPart ​
 +                + aRightValue.mImaginaryPart * aRightValue.mImaginaryPart;​
 +            return *this *
 +                Complex(aRighValue.mRealPart / squareNorm, ​
 +                    - aRightValue.mImaginaryPart / squareNorm);​
 +        }
 +
 +</​code>​
 +</​hidden>​
 +
 ==== Question n°3.3 (optionnel) ==== ==== Question n°3.3 (optionnel) ====
  
-Proposer une surcharge des opérations ​et . Implanter ces dernières et tester.+Proposer une surcharge des opérations ​'​*=' ​et '/​='​. Implanter ces dernières et tester.
  
 <code cpp> <code cpp>
in204/tds/sujets/td5/part1.1571766948.txt.gz · Last modified: 2019/10/22 17:55 by bmonsuez