Langage C++/Méthodes
Les Méthodes :
modifierLa méthode en C++ est l'un des éléments les plus basiques mais essentiels du langage. De nos jours, certains programmes informatiques tels les systèmes d'exploitation contiennent plusieurs millions de méthodes. Comme nous l'avons dit précédemment, la méthode permet de définir une suite d'opérations à exécuter dans un ordre séquentiel donné.
<TypeRetour> [<Portee>::]<NomMethode>([<TypeParametre> <NomParametre>[=<ValeurParDefaut>][,<...>]])
{
[<Instructions>;]
}
Où <TypeRetour> est le type de retour de la méthode <NomMethode>, <Portee> est le nom de la portée à laquelle est rattachée la méthode, s'il y a lieu, <TypeParametre> est le type du paramètre, <NomParametre> est le nom du paramètre, <ValeurParDefaut> est la valeur éventuellement souhaitée pour le paramètre, <...> sont des paramètres additionnels et <Instructions> sont les instructions contenues dans la méthode.
En C++ l’application la plus basique en C++ est le point d'entrée programme qui n'est autre que la méthode "main".
Ceci est le minimum de code requis pour la création d'une application en C++.
Décortiquons un peu cela :
int main(int argc, char* argv[])
Nous avons donc ici le point d'entrée du programme. C'est une méthode qui a comme type de retour une valeur entière. Cette valeur permet au système de savoir dans quelle circonstance s'est terminé le programme. Si le programme s'est fermé normalement, la valeur de retour sera égale à zéro. Par contre si une erreur s'est produite, alors la valeur de retour sera différente de zéro. En général, en cas d'erreur, main retourne -1, mais il peut y avoir d'autres valeurs retournées dans certains cas particuliers.
Le paramètre argc est en fait un compteur sur le nombre de chaines de caractères contenus dans le tableau argv[].
Premières applications :
modifier
Voici un programme d'exemple qui affiche à l'écran une lettre choisie par le programmeur.
#include <iostream> // nécessaire pour utiliser cout.
int main(int argc, char* argv[])
{
// Définit une constante de type unsigned char, dénommée MonCaractere et ayant pour valeur la lettre 'â'.
const unsigned char MonCaractere = 'â';
// Affiche la valeur de la constante MonCaractere sous forme de caractère à l'écran
std::cout << MonCaractere << std::endl;
// Renvoie 0 au système (traitement sans erreur)
return 0;
}
Autre exemple :
#include <iostream> // nécessaire pour utiliser cout.
int main(int argc, char* argv[])
{
//Définit une constante de type char, dénommée MonCaractere et ayant pour valeur la lettre 'â'.
const char MonCaractere = 'â';
//Affiche la valeur de la constante MonCaractere sous forme de caractère à l'écran
std::cout << MonCaractere << std::endl;
//Renvoie 0 au système (traitement sans erreur)
return 0;
}
Les 2 applications produisent le même résultat mais n'ont pas le même code. Cela confirme le fait que la représentation graphique des caractères se fait par le biais de la représentation non signée du codage du caractère.
Bonjour Monde !
modifier#include <iostream> // nécessaire pour utiliser cout
using namespace std;
int main(int argc, char* argv[])
{
cout << "Bonjour Monde !" << endl;
return 0;
}
Bonjour M. X !
modifier#include <iostream> // nécessaire pour utiliser cout
using namespace std;
int main(int argc, char* argv[])
{
cout << "Bonjour M. " << argv[argc - 1] << " !" << endl;
return 0;
}
Voyons ce qui se passe si on exécute la commande DOS: "[PATH]\BonjourMX.exe X". Le programme affiche "Bonjour M. X !".
Si nous exécutons "[PATH]\BonjourMX.exe Dupont", le programme affiche "Bonjour M. Dupont !".
Si nous exécutons "[PATH]\BonjourMX.exe Dupont Dupond" le programme affiche "Bonjour M. Dupond !".
Maintenant que se passerait-il si nous exécutions "[PATH]\BonjourMX.exe" ?
En fait, le programme est prévu pour afficher le dernier paramètre de la chaîne d'appel de la ligne de commande. Comme le système d'exploitation passe au programme le chemin de ce dernier comme premier paramètre, la commande "[PATH]\BonjourMX.exe" affichera "Bonjour M. [PATH]\BonjourMX.exe !".
Appels de Méthodes :
modifierVoici une petite application qui permet de se familiariser avec l'utilité des méthodes. Imaginons que nous voulons rendre le calclul : y = a . x + b, accessible à tout le programme sans avoir à le réécrire partout dans le code. Nous devrons alors écrire la méthode "CalculeAffine" suivante :
#include <iostream> // nécessaire pour utiliser cout
using namespace std;
double mCalculeAffine(double pA, double pX, double pB) // Le petit "m" en préfixe correspond à "method" et le petit "p" à "parameter"(très utile avec les IDE et la complétion de code ([CTRL]+[Espace]))
{
return pA * pX + pB;
}
int main(int argc, char* argv[])
{
double vY; // Le petit "v" en préfixe correspond à "variable" (très utile avec les IDE et la complétion de code ([CTRL]+[Espace]))
double vA = 5;
double vX = 3;
double vB = 2;
cout << "y = " << vA << " . " << vX << " + " << vB << endl; // Affiche "y = 5 . 3 + 2"
vY = mCalculeAffine(vA, vX, vB); // Calcule la fonction mathématique au travers de la méthode;
cout << "y = " << vY << endl; // Affiche "y = 17"
vA = 9;
vX = 3;
vB = 3;
cout << "y = " << vA << " . " << vX << " + " << vB << endl; // Affiche "y = 9 . 3 + 3"
vY = mCalculeAffine(vA, vX, vB); // Calcule la fonction mathématique au travers de la méthode;
cout << "y = " << vY << endl; // Affiche "y = 30"
vA = 4;
vX = 8;
vB = 9;
cout << "y = " << vA << " . " << vX << " + " << vB << endl; // Affiche "y = 4 . 8 + 9"
vY = mCalculeAffine(vA, vX, vB); // Calcule la fonction mathématique au travers de la méthode;
cout << "y = " << vY << endl; // Affiche "y = 41"
vA = 7;
vX = 5;
vB = 2;
cout << "y = " << vA << " . " << vX << " + " << vB << endl; // Affiche "y = 7 . 5 + 2"
vY = mCalculeAffine(vA, vX, vB); // Calcule la fonction mathématique au travers de la méthode;
cout << "y = " << vY << endl; // Affiche "y = 37"
vA = 8;
vX = 9;
vB = 6;
cout << "y = " << vA << " . " << vX << " + " << vB << endl; // Affiche "y = 8 . 9 + 6"
vY = mCalculeAffine(vA, vX, vB); // Calcule la fonction mathématique au travers de la méthode;
cout << "y = " << vY << endl; // Affiche "y = 78"
return 0;
}
Ce code n'est pas trop mal, il fonctionne bien. Seulement, il a un problème, il comporte des doublons de code.
Un vrai développeur n'aurait jamais fait ce programme ainsi. Le copier/coller du code tel que je vous l'ai présenté est consommateur en lignes de code. De plus, il ne facilite pas la lecture du code et peut même introduire des erreurs. Il n'est jamais conseillé de copier/coller du code. Dans un cas sur deux, cela génère des erreurs. |
Voyons comment on pourrait arranger cela de manière plus lisible et moins gourmande en code :
#include <iostream> // nécessaire pour utiliser cout
using namespace std;
double mCalculeAffine(double pA, double pX, double pB)
{
return pA * pX + pB;
}
void mAfficheCalculsAffine(double pA, double pX, double pB)
{
int vY;
cout << "y = " << pA << " . " << pX << " + " << pB << endl; // Affiche "y = pA . pX + pB"
vY = mCalculeAffine(pA, pX, pB); // Calcule la fonction mathématique au travers de la méthode;
cout << "y = " << vY << endl; // Affiche "y = mCalculeAffine(vA, vX, vB)"
}
int main(int argc, char* argv[])
{
double vA = 5;
double vX = 3;
double vB = 2;
mAfficheCalculsAffine(vA, vX, vB); // Affiche "y = 5 . 3 + 2" puis "y = 17"
vA = 9;
vX = 3;
vB = 3;
mAfficheCalculsAffine(vA, vX, vB); // Affiche "y = 9 . 3 + 3" puis "y = 30"
vA = 4;
vX = 8;
vB = 9;
mAfficheCalculsAffine(vA, vX, vB); // Affiche "y = 4 . 8 + 9" puis "y = 41"
vA = 7;
vX = 5;
vB = 2;
mAfficheCalculsAffine(vA, vX, vB); // Affiche "y = 7 . 5 + 2" puis "y = 37"
vA = 8;
vX = 9;
vB = 6;
mAfficheCalculsAffine(vA, vX, vB); // Affiche "y = 8 . 9 + 6" puis "y = 78"
return 0;
}
Récursivité :
modifier
Récursivité Directe :
modifier#include <iostream> // nécessaire pour utiliser cout
using namespace std;
unsigned long mFactorielle(unsigned long pNombre)
{
// Si le parametre "pNombre" vaut 0 alors
if(0 == pNombre) // <- Le test d'arrêt est très important, sans lui la récursion ne j'arrêterais jamais.
{
// Retourner 1 ("0! = 1", Factorielle de 0 est égal à 1)
return 1;
}
else // Sinon
{
// Retourner "pNombre * (pNombre - 1)!", (Factorielle de n = n * Factorielle de (n-1))
return pNombre * mFactorielle(pNombre - 1);
}
}
int main(int argc, char* argv[])
{
unsigned long vNombre = 12; // Valeur entière maximale calculable pour une factorielle sur un unsigned long
unsigned long vFactorielle; // Résultat de la factorielle du nombre.
cout << "n = " << vNombre << endl; // Affiche "n = 12"
vFactorielle = mFactorielle(vNombre); // Calcule la fonction mathématique au travers de la méthode;
cout << "n! = " << vFactorielle << endl; // Affiche "n! = 479001600"
return 0; // Sort du programme.
}
Récursivité Indirecte :
modifier#include <iostream> // nécessaire pour utiliser cout
using namespace std;
bool mNombreImpair(unsigned long pNombre); // nécessaire pour utiliser mNombreImpair dans mNombrePair (déclaration avancée)
// Vérifie si un nombre est pair
bool mNombrePair(unsigned long pNombre)
{
// Si le nombre est égal à 0
if (pNombre == 0)
{
// Retourner vrai
return true;
}
else // Sinon
{
// Retourner la vérification que le nombre "pNombre - 1" est impair
return mNombreImpair(pNombre - 1);
}
}
// Vérifie si un nombre est impair
bool mNombreImpair(unsigned long pNombre)
{
// Si le nombre est égal à 0
if ( pNombre == 0)
{
// Retourner faux
return false;
}
else // Sinon
{
// Retourner la vérification que le nombre "pNombre - 1" est pair
return mNombrePair(pNombre - 1);
}
}
int main(int argc, char* argv[])
{
unsigned int vNombre = 0;
unsigned int vLimite = 15; // Valeur arbitraire
// Tant que vNombre est supérieur ou égal à 0
while(vNombre <= vLimite)
{
cout << "Le nombre \"" << vNombre << "\" est "; // Affiche "Le nombre "(vNombre)" est "
// Si vNombre est pair
if(mNombrePair(vNombre))
{
cout << "pair."; // Affiche "pair."
}
else // Sinon
{
cout << "impair."; // Affiche "impair."
}
cout << endl; // Affiche un retour à la ligne
vNombre++; // Incrémente vNombre
}
return 0; // Sort du programme.
}
Alternative à la Récursivité :
modifierEn C++, il est toujours possible d'écrire un algorithme qui effectue les tache de la récursion de manière itérative.
Pour la factorielle :
#include <iostream> // nécessaire pour utiliser cout
using namespace std;
unsigned long mFactorielleIterative(unsigned long pNombre)
{
unsigned long vResultat = 1;
unsigned long vControle = 1;
while (vControle <= pNombre)
{
vResultat = vResultat * vControle;
vControle++;
}
return vResultat;
}
int main(int argc, char* argv[])
{
unsigned long vNombre = 12; // Valeur entière maximale calculable pour une factorielle sur un unsigned long
unsigned long vFactorielle; // Résultat de la factorielle du nombre.
cout << "n = " << vNombre << endl; // Affiche "n = 12"
vFactorielle = mFactorielleIterative(vNombre); // Calcule la fonction mathématique au travers de la méthode;
cout << "n! = " << vFactorielle << endl; // Affiche "n! = 479001600"
return 0; // Sort du programme.
}
Pour le pair/impair :
#include <iostream> // nécessaire pour utiliser cout
using namespace std;
bool mNombreImpair(unsigned long pNombre);
// Vérifie si un nombre est pair
bool mNombrePairIteratif(unsigned long pNombre)
{
while (true)
{
// Si le nombre est égal à 0
if (pNombre == 0)
{
// Retourner vrai
return true;
}
else // Sinon
{
pNombre--;
if (pNombre == 0)
{
// Retourner la vérification que le nombre "pNombre - 1" est impair
return false;
}
else
{
pNombre--;
}
}
}
}
int main(int argc, char* argv[])
{
unsigned int vNombre = 0;
unsigned int vLimite = 15; // Valeur arbitraire
// Tant que vNombre est supérieur ou égal à 0
while(vNombre <= vLimite)
{
cout << "Le nombre \"" << vNombre << "\" est "; // Affiche "Le nombre "(vNombre)" est "
// Si vNombre est pair
if(mNombrePairIteratif(vNombre))
{
cout << "pair."; // Affiche "pair."
}
else // Sinon
{
cout << "impair."; // Affiche "impair."
}
cout << endl; // Affiche un retour à la ligne
vNombre++; // Incrémente vNombre
}
return 0; // Sort du programme.
}
Cependant, il ne faut pas oublier qu'elle est consommatrice en pile et que cela peut poser problème dans certains systèmes ou pour certaines applications.
Heureusement, la récursivité n'est applicable que dans un certain nombre limité de cas.
Polymorphisme de Méthode
modifierLe polymorphisme de méthode est la faculté qu'ont les méthodes de même nom mais de signatures différentes à pouvoir sélectionner la bonne méthode pour la bonne signature.
#include <stdio.h>
void mToString(char* pString, int pValue)
{
sprintf(pString, "%i", pValue);
}
void mToString(char* pString, unsigned int pValue)
{
sprintf(pString, "%u", pValue);
}
void mToString(char* pString, long pValue)
{
sprintf(pString, "%ld", pValue);
}
void mToString(char* pString, unsigned long pValue)
{
sprintf(pString, "%lu", pValue);
}
int main(int pArgumentsCount, char* pArgumentsValues[])
{
int vInteger = -1123456789;
unsigned int vUnsignedInteger = 2123456789;
long vLong = -3123456789;
unsigned long vUnsignedLong = 4123456789;
char* vString = new char[15];
mToString(vString, vInteger);
printf(vString);
mToString(vString, vUnsignedInteger);
printf(vString);
mToString(vString, vLong);
printf(vString);
mToString(vString, vUnsignedLong);
printf(vString);
delete [] vString;
return 0;
}