Indications pour ceux qui veulent lire vite...
Les titres principaux des parties sont centrés en gras et soulignés. Les sous-parties sont alignées dans la gauche, en gras-italique et marron. Les récapitulatifs sont en bleu. Les codes C++ sont écrit en type "codé" et gras.
I. Les fonctions
1.1 Création d'une fonction
1.2 Utilisation d'une fonction
1.3 La Portée des variables
1.4 Gestion des fonctions et variables globales
1.5 Abus des portées de variable
1.6 Etude de programme
II. Les classes
2.1 Création de classe
2.2 Fonctions de classe
2.3 Constructeur
2.4 Destructeur
I. Les fonctions >> vers le sommaire
1.1 Création d'une fonction >> vers le sommaire
Actuellement, nous avons déjà utilisé une
fonction importante, main()
, et d'autres fonctions comme
sqrt()
. Revenons donc sur leur fonctionnement. Les fonctions
permettent de donner une meilleure structure au programme : par exemple, au
lieu de recopier le code calculant la racine carré, on l'a écrit
une fois dans tout le programme, et on y fait simplement appel par une fonction.
Si vous ne voyez pas où est écrit ce code de calcul de la racine
carré, c'est qu'il se trouve simplement dans un autre endroit du code,
en l'occurence la bibliothèque Math.h
.
On m'a déjà dit : "oui, mais une fonction
renvoit une valeur, et la fonction main()
ne renvoit rien
!". La fonction main()
est très spéciale
: elle est la base du programme. Le programme commence par la fonction main()
,
et lorsque celle-ci se finit, il se finit. Cependant, vous avez remarqué
qu'à chaque compilation de programme nous avions un warning : "warning
C4508: 'main' : function should return a value". Ainsi, si dans nos codes
précédents la fonction main()
ne renvoyait
rien, c'était une erreur ! Nous ne l'avons pas corrigé car j'attendais
de pouvoir la traiter dans un tutorial sur les fonctions.
Ouvrez le projet base et nettoyez-le pour qu'il soit comme ci-dessous
:
Le type d'information renvoyé par une fonction est spécifié
avant le nom de celle-ci. Le type peut-être un de ceux que vous connaissez
(double
, int
, char
,
bool
...) mais on peut aussi spécifier que la fonction
renvoit "rien" par le type void
. Voici un exemple
:
#include <iostream.h>
void main()
{
}
Ce code est tout à fait correct, et aucun warning n'apparaîtra.
void
signifie que la fonction ne renvoit pas d'information,
et c'est exactement ce qu'elle fait, donc tout va. Retenez bien que la fonction
doit impérativement renvoyé une valeur du type prévu
dans sa déclaration. Si le type est void
, alors
elle devra ne rien renvoyer, si le type est int
elle devra
renvoyer une valeur qui appartienne à l'ensemble des int
,
etc. etc. Par exemple :
#include <iostream.h>
int main()
{
return 0;
}
L'opérateur qui permet de retourner une valeur est l'opérateur
return
. Maintenant que le principal est dit, nous allons
créer une nouvelle fonction. Toute autre fonction que main()
doit être déclaré deux fois : une fois en début
de code pour donner ses renseignements (par exemple, son type : s'il s'agit
d'une fonction qui retournera un int
, un char
...)
et ensuite, après la fonction main()
, avec son
contenu. Voici un exemple :
On déclare une première fois la fonction exemple
en début de code, en mettant bien le ;
à
la fin, et on y fait appel dans la suite du code, sans mettre le;
.
La seule différence entre les deux déclarations réside
donc dans ces fameux ;
. En compilant le programme, vous
obtiendrez le résultat suivant :
Récapitulatif sur la création
d'une fonction :
- Le type de la fonction est précisé avant son nom, par exemple
void exemple()
ou int exemple()
- La fonction doit absolument renvoyer, a l'aide de return
,
une information du type prévu. Par exemple, la fonction int
exemple()
aura, à la fin, return 0
.
Le cas particulier est celui de void exemple()
où
on ne mettra rien.
- Toutes les fonctions, sauf la fonction main()
, doivent
être déclarées deux fois. La première fois au début
du code avec un ;
à la fin, et la seconde fois
avec le contenu de la fonction, après main()
, sans
le ;
.
1.2 Utilisation d'une fonction >> vers le sommaire
Il y a deux notions clés avec les informations d'une
fonction : transmettre et recevoir. Pour recevoir les résultats d'une
fonction, voici un exemple :
La variable a
est de type int
et l'information renvoyée par la fonction exemple()
est de type int
, donc a
peut stocker
cette information. Il faut que la variable de stockage ait le même type
que l'information à stocker, c'est assez évident d'ailleurs.
Voici ce que donne l'éxécution du programme :
Si la variable de stockage n'a pas le même type que l'information à stocker, soit cela donne un bug (mais c'est rare), soit le compilateur vous averti qu'il y a une perte d'information, soit le résultat est correct en soi mais peut-être pas celui attendu.
Pour envoyer une information à une fonction, il faut
qu'une variable soit prévue pour réceptionner l'information.
Les variables associées à la fonction se trouvent entre les
parenthèses, voici un exemple :
On veut pouvoir envoyer une information de type int à la fonction.
Dans sa déclaration, on crée donc une variable de type int
qui recevra l'information. Devinez quelle sera la valeur finale de a
? A l'origine a
vaut 5, puis la fonction exemple
lui soustrait 2 et renvoit la nouvelle valeur : a
vaut
donc 3 à la fin du programme.
Récapitulatif sur l'utilisation d'une fonction
:
- Pour recevoir la valeur d'une fonction, on l'attribue simplement à
une variable : a=exemple()
. Il est fortement recommandé
que la variable qui reçoit l'information soit du même type que
l'information renvoyée par la fonction. Par exemple int exemple()
renvoit une information de type int
, la variable devrait
être de type int
.
- Pour envoyer une information à une fonction, il faut qu'une variable
permettant de recevoir l'information soit associée à cette fonction.
Par exemple, pour a=exemple(5);
il faut que la fonction
exemple
puisse recevoir un int
, donc
on rajoute dans les deux déclarations de la fonction int
b
qui recevra l'information : int exemple(int
b)
.
1.3 La portée des variables >> vers le sommaire
Examinez le code suivant :
A première vue, il n'y a rien de spécial. Pas d'erreur de syntaxe,
les fonctions renvoient ce qu'il faut et sont bien déclarées,
la variable a
est bien déclarée. Alors ?
Compilez le programme, le compilateur vous affichera l'erreur suivante : "error
C2065: 'a' : undeclared identifier"
Pourtant, vous avez bien déclaré la variable a
.
Le problème est, en réalité, que les variables ont certaines
portées : elles fonctionnent jusqu'à certains endroits. Quand
on crée une variable dans un bout du code, cette variable existe de
l'endroit où elle a été crée à la fermeture
du bloc auquel elle appartient par un }
. Voici un exemple
:
int a(0);
if(a==0)
{
int b;
}
b=2;
On a crée la variable b
dans un bloc de code (note
: un bloc est l'ensemble commençant à un {
et s'arrêtant au }
le plus proche) qu'on a refermé,
la variable a donc cessé d'exister et lui attribuer une valeur de 2
ne rime plus à rien car elle n'existe plus. Pour revenir à notre
problème avec le fonction exemple()
, c'est exactement
pareil : la variable a
existe dans la fonction main()
et non dans la fonction exemple()
, par conséquent
cette dernière ne peut plus y accéder.
Pour que plusieurs fonctions puissent accéder à une même
variable, il faut que celle-ci soit une variable globale c'est-à-dire
qu'on la déclare en début de code. Voici un code fonctionnel
:
Récapitulatif sur la portée des variables
:
- Une variable crée dans un bout du code, appelée variable "locale",
est utilisable à partir de l'endroit où elle a été
crée jusqu'au }
le plus proche.
- Une variable crée au début du code, en dehors des fonctions,
est appelée variable "globale". Elle est utilisable dans
tout le programme.
1.4 Gestion des fonctions et variables globales >> vers le sommaire
Pour gérer les variables globales et les fonctions, utilisez l'onglet
ClassView
En cliquant sur cet onglet, vous verrez les variables globales et fonctions
:
Vous pourrez ensuite accéder à des fonctionnalités plus
avancées. Faites un clic droit sur la fonction exemple et choisissez
"Called by" pour voir quels sont les endroits du code où
elle a été mentionnée :
Il est très probable que la fonctionalité "Called By"
ne soit pas accessible par défaut, auquel cas vous devrez "Yes"
au message suivant :
Après, vous aurez droit à la fenêtre suivant :
La seconde définition de la fonction exemple se trouve en ligne 14, sa première définition en ligne 2, et on y fait appel en ligne 9. En double cliquant sur une des lignes indiquées, vous serez soit amené à la ligne en question (pour les Definitions), soit le nom de la fonction sera surligné (pour les References). S'il arrive que vous ayez des problèmes dans un code de grande taille, ce genre d'aide au repérage se montrera utile.
Récapitulatif sur la gestion des fonctions et
variables globales :
- En utilisant l'onglet ClassView vous accéderez aux fonctions et variables
globales. A partir d'un clic droit, vous pourrez obtenir, entre autre, les
propriétés et les appels ("Calls", "Called by"...).
- Dans la fenêtre d'appels (Callers Graph), si vous faites un double-clic
sur une ligne de Definition, vous serez emmené à la ligne en
question ; si vous faites un double-clic sur une ligne de Reference, la nom
de l'objet (nom de la variable ou nom de la fonction) sera surligné.
1.5 Abus des portées de variable >> vers le sommaire
Il est possible de créer plusieurs variables avec le même nom
si on fait cette création dans des blocs différents. Le code
suivant est valide :
La seconde variable a
masque la première et, lorsque
la portée de la seconde est finie, on retrouve la première.
On explique souvent ce genre de chose dans les livres, mais attention : si
vous vous risquez à faire cela, votre programme va devenir rapidement
incompréhensible. Il existe un opérateur de résolution
de portée pour atteindre la variable globale si elle est masquée.
Imaginez ce que donne le code suivant à l'affichage :
Entrons directement dans la fonction main()
D'abord,
on crée un a
qui vaut 3 puis on affiche le dernier
a
en date, on va donc afficher 3. Ensuite, si le dernier
a
en date vaut 3 (c'est le cas), on affiche un a
qui vaut 7, donc on va afficher 7. La condition else
ne
se réalisera pas alors car la condition if
s'est
réalisée. La seconde condition if
utilise
l'opérateur de résolution de portée, les ::
désignent la variable globale ; puisque la variable globale vaut 10,
alors on affichera 10. Et ainsi :
Récapitulatif sur les abus des portées
de variable :
- On peut créer plusieurs variables avec le même nom si la création
s'effectue dans des blocs différents, mais c'est très fortement
déconseillé.
- Avec l'opérateur de résolution de portée ::
on fait appel à la variable globale.
1.6 Etude de programme >> vers le sommaire
Si vous souhaitez étudier plus particulièrement les fonctions
et les calculs, voici un programme sympathique à étudier : Aqualonne
Math v1.05. Réalisé en deux jours, il contient quelques
calculs simples de math et vous pourrez en reprendre du code, ou l'améliorer
(si vous repérez des bugs), au besoin. Ses fonctionnalités :
- Calcul de l'expression (a+b)².
- Calcul des coordonnés d'un vecteur AB à partir des coordonnés
des points A et B.
- Calcul d'un nombre à une puissance.
- Calcul des coordonnés du milieu d'un vecteur à partir des
coordonnés du vecteur.
- Détermination d'une fonction à partir de deux valeurs de x
et des deux images.
- Calcul de l'aire d'un carré, d'un rectangle et d'un cercle.
- Calcul du périmetre d'un carré, d'un rectangle, d'un parellogramme,
d'un triangle, d'un trapeze et d'un cercle.
- Vérification si le triangle est rectangle, isocele ou equilateral.
- Signale si la fonction est une fonction inverse.
- Signale si la fonction peut etre une fonction caree ou cube.
- Signale si la fonction peut etre un enchainement inverse et cube ou inverse
et carré.
- Calcul de l'aire d'un parallelepipede rectangle, d'une sphere et d'un cylindre
droit.
- Calcul du volume d'un parallelepipede rectangle, d'une sphere et d'un cylindre
droit.
- Si le triangle est rectangle, on donne cosinus/sinus/tangente de tous les
angles.
A l'intérieur du pack, vous trouverez :
- Les news sur l'évolution du programme.
- Codes sources des version 1.00, 1.01, 1.02, 1.03, 1.04 et 1.05.
- Exécutable du programme.
Je met aussi à votre disposition les bribes d'un programme devant servir à comparer la puissance des voitures par rapport aux distances qu'elles parcouraient selon des distances de recul : Comparateur.
II. Les classes >> vers le sommaire
2.1 Création d'une classe >> vers le sommaire
Dans le tutorial précédent, nous avons crée
des structures. Une fois la structure créée, il était
possible de fabriquer un objet issu de cette structure : les classes permettent
de faire la même chose. Voici un code de structure :
struct human{
int matricule;
int code_postal;
short age;
bool sexe;
};
Et voici un code de classe :
On remplace struct
par class
simplement.
Pour la suite, c'est une question de préférence syntaxique :
on commence toujours le nom d'une classe par C et le nom des membres d'une
classe par m_. On remarquera aussi le public:
. Dans les
classes il y a trois degrés de protection des données, et public:
est le degré le plus bas, c'est des données "publiques",
accessibles à tous.
Pour la suite, on crée un objet sur le modèle de cette classe
comme pour les structures, par exemple :
CHuman Humain;
Ensuite, on accédera au contenu de l'objet en tapant son nom et le
nom de la variable à accéder, séparée par un point.
On remarquera que, une fois le point tapé, une liste s'affiche pour
y choisir la variable, il est préférable de l'utiliser plutôt
que de taper le nom soit-même afin d'éviter les risques de fautes
de frappes :
Voici donc un code simple utilisant une classe :
Récapitulatif sur la création de classe
:
- La création d'une classe est similaire à celle d'une structure.
On crée une classe de la manière suivante :
class CNomclasse
{
public:
int m_contenu;
};
- Dans la syntaxe des classes, le nom d'une classe commence toujours par C
et celui de ses membres par m_.
- Les classes protègent leur contenu à divers degrés.
public:
est le degré de protection le plus faible.
- Pour créer un objet, on tape le nom de la classe suivi du nom de
l'objet à créer. Par exemple CNomclasse Nomobjet;
- Pour accéder au contenu d'un objet on marque son nom puis un point
et le nom de la variable, par exemple Nomobjet.m_contenu
2.2 Fonctions de classe >> vers le sommaire
Les classes peuvent posséder leurs propres fonctions. Celles-ci doivent
être déclarées dans la classe, comme suit :
class CTriangle
{
public:
int m_a;
int m_b;
int m_c;
bool rectangle(int a,int b,int c);
};
Le contenu de la fonction doit suivre la création de la classe. La
seule différence par rapport à précédemment est
que le nom de la fonction doit être précédé du
nom de la classe suivi de ::
, par exemple :
bool CTriangle::rectangle(int a, int b, int c)
{
if (a*a+b*b==c*c || a*a+c*c==b*b || c*c+b*b==a*a)
{
// triangle rectangle
return true;
}
return false;
}
Pour faire appel à la fonction, c'est comme si on faisait appel à
une des variables de la classe. Soit Untriangle l'objet crée sur le
modèle de cette classe, le code pourrait être :
void main()
{
if(Untriangle.rectangle(4,5,6)==true)
{
cout << "Triangle rectangle\n";
}
else
{
cout << "Triangle non rectangle\n";
}
}
Récapitulatif sur les fonctions de classe :
- Elles doivent être déclarées dans la classe, comme les
variables.
- Leur seconde déclaration est celle que nous avons apprise au début
du tutorial, mais avant le nom de la fonction on marque celui de la classe
suivi de ::
- On fait appel à une fonction de classe en marquant le nom de l'objet
crée sur son modèle suivi d'un point et du nom de la fonction,
avec les arguments (c'est-à-dire les valeurs) entre parenthèse
s'il y a lieu.
2.3 Constructeur >> vers le sommaire
Par défaut, le compilateur fournit un constructeur standard quand
nous créons un objet. Pour vous donner une idée de ce qu'est
un constructeur, disons que c'est ce qui donne les valeurs des variables de
l'objet que nous créons. Le constructeur est une fonction se trouvant
en totalité dans la classe et n'ayant pas besoin d'une première
déclaration. Voici comment est le constructeur par défaut :
La fonction du constructeur doit porter impérativement le même
nom que la classe. En éxécutant le programme vous remarquerez
que, même si vous n'avez pas demandé explicitement l'utilisation
du constructeur, le message "Constructeur par defaut." va s'afficher.
En effet, toute création d'un objet passe par l'utilisation du constructeur.
Il est parfois intéressant d'atribuer par défaut des valeurs
aux membres de la classe. Vous pouvez utiliser un constructeur qui attribue
automatiquement les valeurs :
Ici, le constructeur donne automatiquement les valeurs 1, 2 et 3 aux variables
m_a
, m_b
et m_c
.
Vous pouvez aussi utiliser un constructeur qui vous laisse plus de libertés,
en s'en servant comme une fonction. Voici un exemple :
Il vous suffit de spécifier les valeurs lorsque vous créez l'objet, en les indiquant entre parenthèses.
Récapitulatif sur le constructeur :
- Il est utilisé à chaque fois qu'un objet est crée sur
le modèle d'une classe.
- Il s'agit d'une fonction portant le même nom que la classe et se trouvant
en une seule fois dans le programme, contenu dans la classe. Si aucun constructeur
n'est spécifié, le compilateur en rajoute un par défaut,
par exemple pour la classe CTriangle
:
CTriangle()
{
}
- Le but du constructeur est de donner des valeurs aux variables de classe.
On peut utiliser un constructeur statique qui donne automatiquement des valeurs,
par exemple :
class CTriangle
{
public:
int m_a;
CTriangle()
{
m_a=1;
}
- On peut créer un constructeur qui sera véritablement utilisé
en fonction pour plus de liberté, par exemple :
class CTriangle
{
public:
int m_a;
CTriangle(int a)
{
m_a=a;
}
};
CTriangle Untriangle(1);
2.4 Destructeur >> vers le sommaire
De même qu'il existe un constructeur, il existe un destructeur. Voici
un code pour voir le destructeur par défaut :
En éxécutant le code, vous constaterez que le message "Utilisation du destructeur" s'affiche ; il gère la mémoire.
Récapitulatif sur le destructeur :
- Il s'agit d'une fonction portant le même nom que la classe précédé
d'un ~ et se trouvant en une seule fois dans le programme, contenu dans la
classe.
- Si aucun constructeur n'est spécifié, le compilateur en rajoute
un par défaut, par exemple pour la classe CTriangle
:
~CTriangle()
{
}
Nous sommes peut-être allé un peu rapidement sur les classes, qui sont une notion essentielle du langage C++. Je vous recommandes vivement de consulter le programme Matrix pour les étudier davantage. Ce programme est une "console" c'est-à-dire que vous lui donnez et qu'il gère des commandes. Il ne gère que trois commandes : "setmatrix", "setpower" et ";-)matrix". Ce programme a été inspiré par le défilement des caractères célèbre du film Matrix : "setmatrix" règle le nombre de chaînes de caractères qui défileront (plus le nombre est élevé, plus le défilement sera important), "setpower" règle le nombre de chaînes de caractères qui défileront mais à l'envers (plus le nombre est élevé, moins le défilement sera important), enfin ";-)matrix" fait défiler les chaînes.
Vous êtes maintenant formé aux bases du C++. Si vous souhaitez aller plus loin, je propose des tutoriaux pour la programmation graphique windows en C++.
Cliquez ici pour aller au premier tutorial sur la programmation graphique windows en C++ >>