Le C++ est un langage de programmation. Ce langage permet la programmation sous de multiples paradigmes comme, par exemple, la programmation procédurale, la programmation orientée objet et la programmation générique. Au cours des années 1990, le C++ est devenu l'un des langages de programmation les plus populaires dans l'industrie informatique. Personne ne possède le langage C++. Il est libre de droit. Le document de standardisation n'est quant à lui pas disponible gratuitement.

Historique

modifier

Du C au C++

modifier

Bjarne Stroustrup a développé le C++ au cours des années 1980, alors qu'il travaillait dans le laboratoire de recherche Bell d'AT&T. Il s'agissait en l'occurrence d'améliorer le langage C et il l'avait d'ailleurs nommé C with classes (« C avec des classes »). Les premières améliorations se concrétisèrent donc par l'ajout du support des classes, suivies par de nombreuses autres comme les fonctions virtuelles, la surcharge d'opérateurs, l'héritage (simple ou multiple), les « templates », la gestion d'exceptions...

Le langage C++ est normalisé par l'ISO. Sa première normalisation date de 1998 (ISO/CEI 14882:1998), et sa dernière de 2003 (ISO/CEI 14882:2003). La normalisation de 1998 standardise la base du langage (Core Language) ainsi que la bibliothèque standard du C++ (C++ Standard Library).

Le nom signifie « C incrémenté », puisqu'on incrémente une variable (c'est-à-dire qu'on lui ajoute 1) en notant « ++ » derière elle : l'instruction i++, par exemple, est l'équivalent de celle-ci : i=i+1

Il existe de nombreuses autres bibliothèques en C++ non incluses dans le standard. De plus, le C++ permet l'utilisation des nombreuses bibliothèques C existantes.

Fonctionnalités introduites par le C++

modifier

On peut considérer que le C++ « est du C » avec un ajout de fonctionnalités. Une remarque importante est à faire cependant : Certains programmes syntaxiquement corrects en C, ne le sont pas en C++.

Les fonctionnalités ajoutées sont :

  • les déclarations reconnues comme instructions (repris dans C99) ;
  • les opérateurs new et delete pour la gestion d'allocation mémoire ;
  • le type de données bool (booléen) ;
  • les références ;
  • le mot clé const pour définir des constantes (repris par C à la fin des années 1980) ;
  • les fonctions inline ;
  • les paramètres par défaut dans les fonctions ;
  • les référentiels lexicaux (namespace) et l'opérateur de résolution :: ;
  • les classes, ainsi que tout ce qui y est lié : l'héritage, les fonctions membres, les fonctions membres virtuelles, les constructeurs et le destructeur ;
  • la surcharge des opérateurs ;
  • les templates ;
  • la gestion d'exceptions ;
  • l'identification de type pendant l'exécution (RTTI : run-time type identification) ;
  • le commentaire de fin de ligne introduit par « // » (existant dans BCPL, repris dans C99).

La compilation d'un programme en C++ effectue également un contrôle plus minutieux sur le typage.

Histoire du C++

modifier

Stroustrup a commencé à travailler sur du C avec classes en 1979. L'idée de créer un nouveau langage vient de l'expérience en programmation de Stroustrup pour sa thèse de doctorat. Stroustrup trouvait que Simula avait des fonctionnalités très utiles pour le développement de gros programmes mais qu'il était trop lent pour être utilisé en pratique (dû à un problème d'implémentation du compilateur Simula), tandis que BCPL était rapide mais de trop bas niveau et pas adapté au développement de gros logiciels. Quand Stroustrup commença à travailler dans les laboratoires de Bell, on lui demanda d'analyser le noyau UNIX en vue de faire du calcul distribué. Se rappelant sa thèse, Stroustrup commença à améliorer le langage C avec des fonctionnalités similaires à celle de Simula. C a été choisi parce qu'il est rapide, portable et d'usage général. Au départ, classe (avec encapsulation des données), classe dérivée, vérification des types renforcés (typage fort), inlining, et argument par défaut étaient des fonctionnalités ajoutées au C.

Comme Stroustrup développait le C avec classes, il a aussi écrit CFront, un compilateur qui génère du code source C à partir de code source C avec classes (C++). La première commercialisation fut en octobre 1985.

En 1983, le nom du langage fut changé de "C avec classes" en C++. Parmi les nouvelles fonctionnalités qui furent ajoutées au langage, il y avait les fonctions virtuelles, la surcharge des opérateurs et des fonctions, les références, les constantes, le contrôle du typage amélioré et un nouveau style de commentaire (//). En 1985 fut publiée la première édition de The C++ programming Language, apportant ainsi une référence importante au langage qui n'avait pas encore de standard officiel. En 1989, sortie de la version 2.0 du C++. Parmi les nouvelles fonctionnalités, il y avait l'héritage multiple, les classes abstraites, les fonctions membres statiques, les fonctions membres constantes, et les membres protégés. En 1990, The Annotated C++ Reference Manual fut publié apportant les bases du futur standard. Les ajouts de fonctionnalités tardifs comprennent les templates, les exceptions, les espace de noms, les nouvelles conversions et le type booléen.

Comme le langage C++ évoluait, la bibliothèque standard évoluait de concert. La première addition à la bibliothèque standard du C++ fut les flux d'E/S qui apporte les fonctionnalités nécessaires au remplacement des fonctions C traditionnelles telles que printf et scanf. Ensuite, parmi les additions les plus importantes, il y a la Standard Template Library.

Après des années de travail, un comité réunissant l'ANSI et l'ISO standardisa le C++ en 1998 (ISO/CEI 14882:1998). Pendant quelques années après la sortie officielle du standard, le comité traita le rapport de problèmes et publia une version corrigée du standard C++ en 2003.

Un premier programme : Hello world

modifier

Voici l'exemple de Hello world donné dans The C++ Programming Language, Third Edition de Bjarne Stroustrup :

 #include <iostream>

 int main()
 {
     std::cout << "Hello, new world!\n";
 }

Notions de base du C++

modifier

Déclarations, types de base et affectation

modifier

Identificateur

modifier

Chaque variable en C++ doit posséder un identificateur qui est le nom de la variable. Un identficateur est une suite de caractères ou de chiffres, ou d'underscores _. Un identificateur ne peut pas commencer par un chiffre.
Exemple : a, a1, toto, y78, nom_employe


Déclaration

modifier

Chaque variable utilisée en C++ doit être déclarée : la déclaration va fixer le type et l'identificateur de la variable. Le type peut être vu comme la nature des données que contient la variable. Le C++ a un typage strict : le type d'une variable ne peut pas changer au cours de l'exécution du programme.

La syntaxe d'une déclaration est la suivante :
type identificateur


Exemple de déclaration

modifier

int a;
On déclare une variable d'identificateur a de type int (entier).

Types de base

modifier

Un certain nombre de type de base sont définis dans le langage C++ :

  • les types entiers
    • le type int permet de stocker un entier.
    • le type short int permet de stocker un entier mais cet entier devra évoluer dans un plage de valeur plus restreinte que le type int.
    • le type long int permet de stocker un entier mais cet entier devra évoluer dans un plage de valeur plus grande que le type int.
    • le type unsigned int permet de représenter des entiers mais uniquement positif ou nul.
    • il existe aussi les type unsigned short int' et unsigned long int permettant de représenter des entiers positifs ou nul respectivement courts et longs.


Une des grosse difficulté avec le C++ et que le nombre de bits ssur lesquels sont représentés les types int, short et long ne sont pas précisés. Ainsi un int peut être représenté aussi bien sur 32 bits que sur 64 bits. Ceci pose de gros problème de portabilité. La seule chose qui est spécifiée est qu'un long int est plus long qu'un int et qu'un short int est plus court qu'un int. Cela peut causer de gros soucis de portabilité.

  • les types réels
    • le type float  : il permet de représenter des réels en simple précision.
    • le type double : il permet de représenter des réels en double précision.

Les systèmes de représentation utilisés par les double et les int ne sont pas précisés.


  • le type char : permet de représenter un caractère. Le système de représentation (souvent le coage ASCII) n'est pas spécifié ni le nombre de bits utilisés (souvent 8 bits).


  • le type bool : le type bool permet de représenter un booléen c'est à dire une valeur pouvent valoir soit true soit false.



L'affectation

modifier
  • Syntaxe :

identificateur=expression

  • sémantique

On commence par évaluer l'expression, on stocke ensuite sont résultat dans la variable s.

Les opérateurs

modifier
  • sur les entiers :
    • les symboles +, - , * et / sont utilisés pour faire respctivement des additions, soustractions, multiplications et divisions sur des entiers. La division sur les entiers entraîne une troncature.
    • le symbole % permet de faire le module. 8%3 est 8 modulo 3 c'est-à-dire le résultat de la division de 8 par 3 c'est-à-dire 2.
    • on peut utiliser les parenthèses pour préciser l'ordre des opérations.


  • sur les réels :
    • les symboles +, - , * et / sont utilisés pour faire respctivement des additions, soustractions, multiplications et divisions sur des réels.
    • on peut utiliser les parenthèses pour préciser l'ordre des opérations.


  • sur les booléens :
    • le test d'égalité utilise le symbole ==
    • le test différent utilise le symbole !=
    • le test strictement supérieur utilise le symbole >
    • le test supérieur ou égal utilise le symbole >=
    • le test strictement inférieur utilise le symbole <
    • le test inférieur ou égal utilise le symbole <=
    • le symbole && représente le ET logique.
    • le symbole || représente le OU logique.
    • le symbole ! représente le NON logique.
    • on peut utiliser les parenthèses pour préciser l'ordre des opérations.

Exemple de programme

modifier
#include <iostream>
using namespace std;

int main()
{
     int a,b,s;
     cout<<"Tapez la valeur de a : ";cin>>a;
     cout<<"Tapez la valeur de n : ";cin>>b;
     s=a+b;
     cout<<"La somme a+b vaut : "<<s<<endl;

     return 0;
}

Exécution :
Tapez la valeur de a : 45
Tapez la valeur de b : 67
La somme a+b vaut 112

Explications : dans ce programme, on déclare 3 variables a, b et c. On demande à l'utilisateur du programme de taper la valeur de a puis la valeur de b. cout sert à l'affichage à l'écran et cin à la saisie au clavier. Le programme calcule ensuite dans la variable s la somme a+b. On affiche finalement la valeur de s.


Les pointeurs

modifier

Un pointeur correpond à l'adresse en mémoire où est stockée une variable d'un certain type. Il permet d'accéder indirectement à une variable. Il permet également de construire des structures de données complexes.

Déclaration

modifier

type * identificateur;
La variable identificateur est un pointeur vers une variable de type type.

L'opérateur &

modifier

Il permet d'obtenir un pointeur vers une variable.
&identificateur permet d'obtenir un pointeur vers la variable identificateur.

L'opérateur *

modifier

Il permet de déréférencer un pointeur c'est-à-dire à partir d'un pointeur il permet d'obtenir la valeur de la variable pointée.
*pointeur permet d'obtenir la valeur de la variable pointée par pointeur.

Exemple de programme

modifier
#include <iostream>
using namespace std;

int main()
{
     int a,b,c;
     int *x,*y;
     
     a=98;
     b=108;

     x=&a;
     c=*x+5;
          
     y=&b;
     *y=a+10;

     cout<<"La variable b vaut : "<<b<<endl;
     cout<<"La variable c vaut : "<<c<<endl;

     return 0;
}

Exécution :
La variable b vaut 88
La variable c vaut 103

Explications :

  • dans ce programme, on déclare 3 variables a, b et c. On déclare ensuite 2 pointeurs vers des entiers x et y.
  • a est initialisé à 98 et b à 78.
  • x=&a; permet de mettre dans x l'adresse de a. x est désormais un pointeur vers a.
  • x est la valeur de la variable pointée par x, c'est-à-dire la valeur de a, et vaut donc 98.

c=*x+5; permet donc de tranférer 98+5 donc 103 dans la variable c.

  • y=&b permet de mettre dans y l'adressee la variable b. y est désormais un pointeur vers b.

a+10 vaut 98+10 donc 108.
*y=a+10; permet de tranférer dans la variable pointée par y la valeur de a+10, c'est-à-dire 108. On transfère donc 108 dans b.

  • on affiche ensuite les valeurs de b et c c'est à dire respectivement 108 et 103.



Les références

modifier

Les référence peuvent être vue comme un moyen d'accès indirect à une variable.

Déclarations

modifier

Syntaxe :
type & identificateur1=identificateur2;

Sémantique

modifier

La variable identificateur1 est une référence vers la variable identificateur2. La variable identificateur2 doit être de type type.

Exemple de programme

modifier
#include <iostream>
using namespace std;

int main()
{
     int a,b,c;

     a=98;
     b=78;

     int &x=a;
     c=x+5;
          
     int &y=b;
     y=a+10;

     cout<<"La variable b vaut : "<<b<<endl;
     cout<<"La variable c vaut : "<<c<<endl;

     return 0;
}

Exécution
La variable b vaut : 108
La variable c vaut : 103
Explications

  • Dans ce programme, on définit 3 variables entières a, b et c et on initialise a à 98 et b à 78.
  • int &x=a; permet de déclarer une référence x vers la variable a.

x+5 vaut donc la même chose que a+5 donc 103.
c=x+5; permet donc de transférer 103 dans la variable c.

  • int &y=b; permet de déclarer une référence y vers la variable b.

a+10 vaut 98+10 donc 108.
y=a+10; permet de transférer 108 dans la variable b.

  • on affiche ensuite b et c c'est-à-dire respectivement 108 et 103.



Les structures de contrôle

modifier

Le structures de contrôle permettent d'interrompre l'exécution séquentielle des différentes instructions d'un programme pour effectuer des tests (les conditionnelles) ou pour répéter certaines partie d'un programme (les boucles). La notion de structure de contrôle est une notion fondamentale en programmation et est à la base de toute l'algorithmiqe. En C++, on trouve comme structure de contrôle :

  • trois structures de contrôle conditionnelles :
    • le if.
    • le if...else....
    • le switch.
  • trois structures de contrôle répétitives :
    • le for.
    • le while.
    • le do...while.
  • deux autres structure de contrôle un peu particulière le goto et le break.


Cette structure de contrôle permet d'exécuter une instruction ou une suite d'instructions seulement si une condition est vraie.

  • Syntaxe :

if(condition) instruction;

  • Sémantique :

On évalue la condition :
- si elle est vraie on exécute l’instruction et on passe à l ’instruction suivante.
- si elle est fausse on passe directement à l ’instruction suivante.
L ’instruction peut être remplacée par une suite d ’instructions entre accolades

  • Exemple :
#include <iostream>
using namespace std;

int main()
{
  int a;
  cout << "Tapez la valeur de a : "; cin >> a;
  if (a > 10) cout << "a est plus grand que 10" << endl;
  cout << "Le programme est fini" << endl;
  return 0;
}

Ce programme demande à l'utilisateur de taper un entier a. Si a est strictement plus grand que 10, il affiche "a est plus grand que 10". Dans le cas contraire, il n'affiche rien.
Exécution 1 :
Tapez la valeur de a : 12
a est plus grand que 10
Exécution 2 :
Tapez la valeur de a : 8



Le if ...else...

modifier

Cette structure de contrôle permet d'exécution soit l'instruction1, soit l'instruction2 en fonction du résultat d'une condition.

  • Syntaxe :

Syntaxe : if(condition) instruction1; else instruction2;

  • Sémantique :

1) On évalue la condition,
2) si elle est vraie, on exécute l ’instruction1 et on passe à l ’instruction suivante.
3) si elle est fausse, on exécute l ’instruction2 et on passe à l ’instruction suivante.
L’instruction1 ou l ’instruction2 peuvent être remplacées par des suite d ’instructions entre accolades .

  • Exemple :
#include <iostream>
using namespace std;

int main()
{
  int a;
  cout << "Tapez la valeur de a : "; cin >> a;
  if (a > 10) cout << "a est plus grand que 10" << endl;
         else cout << "a est plus inférieur ou égal à 10" << endl;
  return 0;
}

Ce programme demande à l'utilisateur de taper un entier a. Si a est strictement plus grand que 10, il affiche "a est plus grand que 10". Dans le cas contraire, il affiche "a est plus inférieur ou égal à 10" .
Exécution 1 :
Tapez la valeur de a : 12
a est plus grand que 10
Exécution 2 :
Tapez la valeur de a : 8
"a est plus inférieur ou égal à 10"



Le switch

modifier
  • Syntaxe :
switch(identificateur)
{
case c1:instruction1;break;
case c2:instruction2;break;
case c3:instruction3;break;
...
default: instruction;break; 
} 
  • Sémantique du switch :

On teste la variable définie par l'identificateur. On la compare successivement aux constantes c1, c2, c3,…etc… Si la variable vaut c1 alors on exécute l’instruction1 et on passe à l’instruction suivante. Si elle vaut c2, on exécute l'instruction2 et on passe à l’instruction suivante. Idem s'il vaut c3. Si elle ne vaut ni c1, ni c2, ni c3 alors on exécute l'instruction après default et on passe à l’instruction suivante. Le default est facultatif. On peut remplacer les instructions instruction1, instruction2, instruction3 par des suites d'instructions sans mettre d'accolades. Les valeurs c1, c2,c3 .. sont obligatoirement des constantes.

  • Exemple
#include <iostream>
using namespace std;

int main()
{
  int a;
  cout << "Tapez la valeur de a : "; cin >> a;
  
  switch(a)
  {
  case 1 : cout<<"a vaut 1"<<endl; break ; 
  case 2 : cout<<"a vaut 2"<<endl; break ; 
  case 3 : cout<<"a vaut 3"<<endl; break  ;
  default : cout<<"a ne vaut ni 1, ni 2, ni 3"<<endl; break  ;                       
  }
  return 0;
}

Ce programme demande à l'utilisateur de taper une variable a. On tete ensuite la valeur de a : en fonction de cette valeur on affiche respectivement les messages "a vaut 1" , ou "a vaut 2" , ou "a vaut 3" ,ou "a ne vaut ni 1, ni 2, ni 3".
Exécution 1 :
Tapez la valeur de a : 1
a vaut 1
Exécution 2 :
Tapez la valeur de a : 2
a vaut 2
Exécution 3 :
Tapez la valeur de a : 3
a vaut 1
Exécution 4 :
Tapez la valeur de a : 11
a vaut ne vaut ni 1, ni 2, ni 3


Le while

modifier
  • Syntaxe :

while(condition)instruction;

  • Sémantique du do … while

1) On teste la condition :
2) si elle est vraie, on exécute l’instruction puis on recommence au 1).
3) si elle est fausse, on passe à l’instruction suivante.
L’instruction peut être une suite d ’instructions entre accolades.

  • Exemple de programme
#include <iostream>
using namespace std;

int main()
{
int i=0;
while(i<10)
    {
    cout<<"La valeur de i vaut : "<<i<<endl;
    i++;
    }
cout<<"La valeur finale de i vaut : "<<i<<endl;
return 0;
} 

Exécution :
La valeur de i est : 0
La valeur de i est : 1
La valeur de i est : 2
La valeur de i est : 3
La valeur de i est : 4
La valeur de i est : 5
La valeur de i est : 6
La valeur de i est : 7
La valeur de i est : 8
La valeur de i est : 9
La valeur finale de i est : 10
Explications
La variable i est initialisée à 0.
A chaque étape, à la fin du corps du while, on incrémente i de 1.
On exécute donc le corps du while la première fois avec i valant 0, la dernière fois avec i valant 9.
Lorsqu’on sort du while i vaut 10.



Le do ... while....

modifier
  • Syntaxe :

do {instruction; } while(condition);

  • Sémantique :

1) on exécute l’instruction.
2) on évalue la condition.
3) si elle est vraie, on recommence au 1.
4) si elle est fausse, on passe à l ’instruction suivante.

  • Exemple
#include <iostream>
using namesapce std;

int main()
{
int i=0;
do {
   cout<<"La valeur de i vaut : "<<i<<endl;
   i=i+1;
   }while(i<10);
cout<<"La valeur finale de i est "<<i<<endl;
return 0;
}

Exécution : La valeur de i vaut : 0
La valeur de i vaut : 1
La valeur de i vaut : 2
La valeur de i vaut : 3
La valeur de i vaut : 4
La valeur de i vaut : 5
La valeur de i vaut : 6
La valeur de i vaut : 7
La valeur de i vaut : 8
La valeur de i vaut : 9
La valeur finale de i est : 10



Le for est une structure de contrôle qui permet de répéter un certain nombre de fois une partie d'un programme.

  • Syntaxe :
for(instruction1; condition; instruction2)
   instruction3;
  • Sémantique du for :

1) on exécute l’instruction1.
2) On teste la condition :
- si elle est vraie, on exécute l’instruction3, puis l’instrution2 puis on revient au 2).
- si elle est fausse on passe à l’instruction suivante.
L’instruction3 peut être une suite d ’instructions entre accolades.

  • Exemple
#include <iostream>
using namespace std;
int main()
{
int i;
for(i=0;i<10;i=i+1)
    cout<<"BONJOUR"<<endl;
return 0;
} 



Le goto

modifier



Les tableaux

modifier

Les tableaux sont des structures de données constituées d'un certain nombre de case. On peut accéder directement au contenu d'une case en indiquant l'indice de la case.

Les tableaux statiques

modifier
  • Syntaxe

type identificateur[taille]

  • Sémantique :

identificateur est un tableau de taille cases contenant chacune un élément de type type. taille est obligatoirement une valeur constante. La taille du tableau est donc figée une fois pour toute et ne peut pas être modifiée en cours d'exécution. Pour désigner la i-ième case de tableau, on écrit identificateur[i] où i est un entier quelconque. Les cases sont numérotées à partir de 0 : les valeurs possibles de i vont donc de 0 à taille-1.

  • Pointeurs et tableaux

Il existe un lien entre les pointeurs et les tableaux : identificateur est en fait un pointeur constant vers un élément de type type.

  • Exemple
#include <iostream>
using namespace std;

int main()
{
  int i;
  int t[10];
  
  for(i=0;i<10;i++)
    t[i]=i*i;
  
 for(i=0;i<10;i++)
     cout<<t[i]<<endl;
     
  return 0;
}

Exécution :
0
1
4
9
16
25
36
49
64
81


Les tableaux dynamiques

modifier

Un tableau dynamique est un tableau dont le nombre de cases peut varier au cours de l'exécution du programme. Il permet d'ajuster la taille du tableau au besoin du programmeur.

  • L'opérateur new

Syntaxe : pointeur=new type[taille];
Cette utilisation du new permet de demander un tableau de taille cases contenant chacune un élement de type type. La variable taille est un entier qui peut être quelconque. new reverra un pointeur vers un type. La variable pointeur est donc du type type *. Les cases du tableaux seront numérotées de 0 à taille-1 et on y accédera comme un tableau statique. S'il n'y a pas assez de mémoire disponible, new renvoie le pointeur NULL.

  • L'opérateur delete

Syntaxe : delete []pointeur;
Cette utilisation de delete permet de détruire un tableau précédemment alloué grâce à new. Le programmeeur en C++ doit gérer la destruction effective des tableaux qu'il a créé dynamiquement.

  • Exemple
#include <iostream>
using namespace std;

int main()
{
  int i,taille;
  cout<<"Tapez la valeur de taille : ";cin>>taille;
  int *t;
  t=new int[taille];
  
  for(i=0;i<taille;i++)
    t[i]=i*i;
  
  for(i=0;i<taille;i++)
     cout<<t[i]<<endl;
  delete []t;
  
  return 0;
}

Exécution 1 :
Tapez la valeur de taille : 4
0
1
4
9
Exécution 2 :
Tapez la valeur de taille : 6
0
1
4
9
16
25



Les structures

modifier

Présentation

modifier

Les structures permettent de regrouper dans une même entité plusieurs variables. Ainsi il est possible de construire de nouveaux types plus complexes.

Syntaxe

modifier
struct identificateur
{
//liste des différents champs constituant notre structure


};

Nous définissions ici une structure appelée identificateur.

Exemple

modifier
#include <iostream>
#include<cmath>
using namespace std;

struct Point
{
double x;       
double y; 
};

int main()
{
  Point A,B;
  double dx, dy,distance;
  
  cout<<"Tapez l'abscisse de A : ";cin>>A.x;
  cout<<"Tapez l'ordonnée de A : ";cin>>A.y;
  cout<<"Tapez l'abscisse de B : ";cin>>B.x;
  cout<<"Tapez l'ordonnée de B : ";cin>>B.y;
  
  dx=A.x-B.x;
  dy=A.y-B.y;
  distance=sqrt(dx*dx+dy*dy);
  
  cout<<"La distance AB vaut : "<<distance<<endl;

  return 0;
}


Les fonctions

modifier

Le C++ est un langage procédural, on peut définir des fonctions qui vont effectuer une certaine tâche. On peut paramètrer des fonctions qui vont permettre de paramétrer cette tâche et rendre ainsi les fonctions réutilisables dans d'autres contextes. Une fonction pourra appeler d'autres fonctions et ainsi de suite. Une fonction peut même s'appeler elle-même : on parle alors de fonctions récursives.

Prototype d'une fonction

modifier

Définition d'une fonction

modifier

Passage de pointeurs en paramètre

modifier

Passage de références en paramètre

modifier

Exemple

modifier


La programmation orientée objet en C++

modifier

Le C++ utilise les concepts de la programmation orientée objet et permet entre autres :

  • La classification,
  • L'encapsulation,
  • La composition de classes,
  • L'association de classes,
  • L'héritage, qui permet le polymorphisme,
  • L'abstraction,
  • La généricité.

La notion de classe

modifier

Une classe permet de regrouper dans une même entité des données membres et des fonctions permettant de manipuler ces données appelées méthodes. La classe est la notion de base de la programmation orientée objet. Il s'agit en fait d'une évolution de la notion de structure mais la classe apporte de nouvelles notions orientées objet absolument fondamentales. Un objet est un élément d'une certaine classe : on parle de l'instance d'une classe.

L'encapsulation en C++

modifier

L'encapsulation est un mécanisme qui interdit d'accéder à certaines données depuis l'extérieur de la classe. Ainsi, un utilisateur de la classe ne pourra pas accéder à tous les éléments de celles-ci. Il sera obligé d'utiliser certaines fonctions membres de la classe (celles qui sont publiques). L'avantage de cette restriction est qu'il empêche par exemple un utilisateur de la classe de mettre les données dont un état incohérent. Vue de l'extérieur, la classe apparait comme une boîte noire qui a un certain comportement à laquelle on ne peut accéder que par les méthodes publiques. Cette notion est extrêmement puissante et permet de nombreux effets de bord.


L'encapsulation permet de distinguer très nettement ce que fait la classe et sa sémantique précises de la manière dont on l'implémente. Cette réflexion permet de répondre dans un premier temps à la question "Comment on utilise la classe ?". Ce n'est que dans un second temps que l'aspect technique entre en jeu et que le programmeur doit répondre à la question "Comment vais-je programmer les fonctionnalités de la classe qui ont été spécifiées". L'encapsulation permet de faire abstraction du fonctionnement interne (c'est-à-dire, l'implémentation) d'une classe et ainsi de ne se préoccuper que des services rendus par celle-ci.


Le C++ implémente l'encapsulation en permettant de déclarer les membres d'une classe avec le mot réservé public, private ou protected. Ainsi, lorsqu'un membre est déclaré :

  • public, il sera accessible depuis n'importe quelle fonction.
  • private, il sera uniquement accessible d'une part, depuis les fonctions qui sont membres de la classe et, d'autre part, depuis les fonctions autorisées explicitement par la classe (par l'intermédiaire du mot réservé friend). Ces dernière fonctions sont appelées fonctions amies de la classe.
  • protected, il aura les mêmes restrictions que s'il était déclaré private, mais il sera en revanche accessible par les classes filles.


Le C++ n'impose pas l'encapsulation des membres dans leurs classes. On pourrait donc déclarer tous les membres publics, mais en perdant une partie des bénéfices apportés par la programmation orientée objet. Il est de bon usage de déclarer toutes les données privées, ou au moins protégées, et de rendre publiques les méthodes agissant sur ces données. Ceci permet de cacher les détails de l'implémentation de la classe.

Constructeurs et destructeurs

modifier
  • Constructeurs
    • Lorsqu'on crée une nouvelle instance d'une classe, les données membres de cette instance ne sont pas initialisées par défaut. Un constructuer est une méthode qui sera appelée au moment de la création d'une nouvelle instance d'une classe. Il peut avoir plusieurs constructeurs d'une classe avec différents paramètres qui serviront à initialiser notre classe. Le constructeur d'une classe A est appelé automatiquement lorsqu'on crée un instance en écriavant : A toto; ou encore A toto(6,9);. Ils sont également appelé lorsqu'une intsnace est créée grâce à l'opérateur new.
    • Syntaxe :

A(); où A est le nom de la classe.
A(paramètres);

  • Destructeurs
    • Les destructeurs sont appelés lorsqu'une instance d'une classe est détruite. Cela arrive à la fin de l'exécution d'une méthode : si vous avez déclaré , localement à une méthode un élément d'un classe A par A toto; alors à la fin de l'appel de la méthode, le destructeur éventuelle de la classe est appelé. Il est également appelé lorsqu'on détruit une instance grâce à delete. Il ne peut y avoir qu'un seul destructeur pour une même classe.
    • Syntaxe :

~A(); où A est le nom de la classe.

Les opérateurs new et delete

modifier

Il est parfois intéressant de crée dynamiquement de nouvelles instances d'une classe. cela s'avère indispensable lorsque vous manipulez certaines structures de données complexes. L'opérateur new permet de créer un nouvelle instance d'une classe à en écrivant
pointeur=new A();
La variable pointeur doit alors être du type A *.

Exemples de classes

modifier

Exemple 1

modifier
  • Fichier Point.h
#ifndef POINT_H
#define POINT_H

#include<iostream>
using namespace std;

class Point
{
public:
// Constructeurs
Point();
Point(double x, double y);

//Accesseurs et mutateurs
void setX(double x);
void setY(double y);
double getX();
double getY();

// Autres méthodes
double distance(const Point &P);
Point milieu(const Point &P);

private: 
double x,y;
}; 
#endif
  • Fichier Point.cpp
#include "Point.h"
#include<cmath>
#include <iostream> 
using namespace std;

Point::Point() : x(0),y(0)
{
} 

Point::Point(double x, double y) : x(x),y(y)
{
} 

void Point::setX(double x)
{
this->x=x; 
}
void Point::setY(double y)
{
this->x=x; 
}

double Point::getX()
{
return x;
} 

double Point::getY()
{
return y; 
} 

double Point::distance(const Point &P)
{
double dx,dy;
dx=x-P.x;
dy=y-P.y;
return sqrt(dx*dx+dy*dy); 
}

Point Point::milieu(const Point &P)
{
Point M;
M.x=(P.x+x)/2; 
M.y=(P.y+y)/2; 
return M;
}

void Point::saisir()
{
cout<<"Tapez l'abscisse : ";cin>>x;
cout<<"Tapez l'ordonnée : ";cin>>y;
} 

void Point::afficher()
{
cout<<"L'abscisse vaut "<<x<<endl;
cout<<"L'abscisse vaut "<<y<<endl;
} 
  • Fichier main.cpp
#include <iostream> 
using namespace std;
#include"Point.h"

int main() 
{ 
Point A,B,C;
double d;
cout<<"SAISIE DU POINT A"<<endl;
A.saisir();
cout<<endl;
cout<<"SAISIE DU POINT B"<<endl;
B.saisir();
cout<<endl;
C=A.milieu(B);
d=A.distance(B);
cout<<"MILIEU DE AB"<<endl;
C.afficher();
cout<<endl;
cout<<"La distance AB vaut :"<<d<<endl;
return 0;
} 

Exemple 2

modifier

Exemple de la déclaration de la classe MessageInternet comportant des attributs privés et des méthodes publiques dont le constructeur 'MessageInternet'.

class MessageInternet
{
 private:
  string m_sujet;
  string m_expediteur;
  string m_destinataire;
 public:
  MessageInternet (string sujet, string expediteur, string destinataire);
  string sujet ();
  string expediteur ();
  string destinataire ();
};

Surcharge d'opérateurs

modifier

Présentation

modifier

Pour certaine classe, la notion d'addition ou de multiplication est totalement naturelle. La surcharge d'opérateurs permet d'écrire directement U+V lorsqu'on veut additionner deux instances U et V d'une même classe A.

Syntaxe

modifier

Pour surcharge l'opérateur+ de la classe A, il suffit de créer une méthode :
A operator+(A Fraction & f);

Exemple

modifier
  • Fichier Fraction.h
#ifndef FRACTION_H
#define FRACTION_H

#include<iostream>
using namespace std;

class Fraction
{
friend ostream & operator<<(ostream & out, const Fraction &f);
friend istream & operator>>(istream &in, Fraction &f); 

public:
    Fraction();
    Fraction(int i);
    Fraction(int num,int den);
    
    Fraction operator+(const Fraction & f);
    Fraction operator-(const Fraction & f);
    Fraction operator*(const Fraction & f);
    Fraction operator/(const Fraction & f);
private:
    int num,den;    
    int pgcd(int x, int y);
    void normalise();
};

#endif
  • Fichier Fraction.cpp
#include"Fraction.h"
#include<sstream>

Fraction::Fraction():num(0),den(1)
{
}

Fraction:: Fraction(int i):num(i),den(1)
{
}    

Fraction::Fraction(int num,int den) : num(num),den(den)
{
normalise();
}   

ostream & operator<<(ostream & out, const Fraction &f)
{
if(f.den!=1)out<<f.num<<"/"<<f.den;
    else out<<f.num;
return out;
} 

istream & operator>>(istream &in, Fraction &f)
{
string s;
bool ok=true;
do
{
cout<<"Tapez le numerateur : ";getline(in,s);
istringstream is1(s);
ok=(is1>>f.num) && is1.eof();
}while(!ok);
do
{
cout<<"Tapez le denominateur : ";getline(in,s);
istringstream is2(s);
ok=(is2>>f.den)&& is2.eof();
}while(!ok);
f.normalise();
}
    
int Fraction::pgcd(int x, int y)
{
int r;
if(x<=0 || y<=0)r=-1;
else{
    while(x!=0 && y!=0 && x!= y)if(y>x)y=y%x; else x=x%y;
    if(x==0)r=y; else r=x;
    }      
}

void Fraction::normalise()
{
int s,n,d,p;
if(den<0){s=-1;d=-den;}else {s=1;d=den;}
if(num<0){s=-s;n=-num;} else n=num;
if(n!=0)
    {
    if(d !=0){p=pgcd(n,d);n=n/p;d=d/p;num=n*s;den=d;}
    }   
    else {num=0;den=1;}
}   

Fraction Fraction::operator+(const Fraction & f)
{
Fraction r;   
r.num=f.den*num+den*f.num;
r.den=f.den*den;
r.normalise();
return r;
}    

Fraction Fraction::operator-(const Fraction & f)
{
Fraction r;   
r.num=f.den*num-den*f.num;
r.den=f.den*den;
r.normalise();
return r;
}  

Fraction Fraction::operator*(const Fraction & f)
{
Fraction r;   
r.num=f.num*num;
r.den=f.den*den;
r.normalise();
return r;
}    

Fraction Fraction::operator/(const Fraction & f)
{
Fraction r;   
r.num=f.den*num;
r.den=f.num*den;
r.normalise();
return r;
}  
  • Fichier main.cpp
#include<iostream>
using namespace std;

#include"Fraction.h"

int main()
{
Fraction f1,f2,f3,f4,E;
cout<<"SAISIE de f1 "<<endl;cin>>f1;
cout<<"SAISIE de f2 "<<endl;cin>>f2;

f3=Fraction(3,4);
f4=Fraction(5,8);

E=(f1+f3-f2)/(f1*f2-f4)+4;

cout<<"E="<<E<<endl;

return 0;
}



Héritage

modifier

Présentation

modifier

A partir d'une classe A, on peut créer un classe B qui possède toutes les caractéristiques de la classe A mais on a rajouté un certains nombre de méthodes qui sont spécifique à A. Cette notion orientée objet fondamentale s'appelle l'héritage.


Il existe en fait 3 types d'héritage public, private ou protected qui permet de spécifier si oui ou non une méthode de le classe B peut modifier une données membres de la classe A, selon qu'elle soit public, private ou protected.

Syntaxe

modifier
class B : type_héritage A
{
.......
};

type_héritage est le mot clé public, protected ou private.

Exemple

modifier

Classes abstraites

modifier

Présentation

modifier

Une classe abstraite est une classe pour laquelle on a défini une méthode mais on a explicitement indiqué qu'on ne fournira aucune implémentation de cette méthode. Il est interdit de créer une instance d'une classe abstraite. Ce mécanisme est extrêmement puissant pour manipuler des concepts abstraits. On peut même avoir une classe pour laquelle toutes les méthodes sont abstraites, on parle alors de classe abstraite pure.

Syntaxe

modifier

type nom_méthode(paramètres) =0

Exemple

modifier

La généricité avec les templates

modifier

À quoi servent les templates ?

modifier

Avec les templates, on peut écrire des fonctions et des classes en paramétrant le type de certains de leurs constituants (type des paramètres ou type de retour pour une fonction, type des éléments pour une classe collection par exemple). Les templates permettent d'écrire du code générique, c'est-à-dire qui peut servir pour une famille de fonctions ou de classes qui ne diffèrent que par la valeur de ces paramètres.

Pourquoi utiliser des templates ?

modifier

En programmation, il faut parfois écrire de nombreuses versions d'une même fonction ou classe suivant les types de données manipulées.

Par exemple, un tableau de int ou un tableau de double sont très semblables, les fonctions de tri ou de recherche dans ces tableaux sont identiques au type près.

En résumé, l'utilisation des templates permet de "paramétrer" le type des données manipulées.

Avantages à utiliser des templates

modifier
  • écritures uniques pour les fonctions et les classes.
  • moins d'erreur dû à la réécriture (copier-coller assassin!).
  • constitution d'une bibliothèque de classes et de fonctions réutilisables dans d'autres contextes.

Exemple de templates

modifier

Dans la bibliothèque standard C++, il y a de nombreux templates. On citera à titre d'exemple, les entrée/sorties, les chaînes de caractères ou les containers. Les classes string, istream, ostream et iostream sont toutes des instanciations de char type.

Les fonctions de recherche et de tri sont aussi des templates écrites et utilisables avec de nombreux types.

// La fonction template Max peut être appelée avec tout type
// copiable et comparable avec l'opérateur <.
template <typename T> T Max(T a, T b)
{
    return a < b ? b : a;
}

#include <string>
int main()
{
    int i = Max(3, 5);
    char c = Max('e', 'b');
    std::string s = Max(std::string("hello"), std::string("world"));
    float f = Max<float>(1, 2.2);
}

La bibliothèque standard (C++ standard library)

modifier

La bibliothèque standard du C++ est en grande partie un surensemble des fonctions disponibles dans la bibliothèque standard du C. Elle englobe la STL qui met à la disposition du programmeur des outils puissants comme les collections (conteneurs) et les itérateurs.

A l'origine, la STL était une bibliothèque développée par Hewlett-Packard. Dans la norme, celle-ci n'est pas appelée STL, car elle est considérée comme faisant partie de la bibliothèque standard du C++. Toutefois, beaucoup de personnes l'appellent encore de cette manière pour distinguer d'une part, les fonctions d'entrées/sorties comprises dans cette bibliothèque et, d'autre part, celles fournies par la bibliothèque C.

Comme pour le C, l'utilisation d'une bibliothèque se fait par l'intermédiaire de la directive #include (suivie du nom du fichier d'en-tête).


Bibliographie

modifier

Liens externes

modifier

Modèle:Wikibooks