Une introduction au langage C++ Marc Jachym, Lurpa Janvier 2008
À l origine, le langage C Langage procédural créé dans les années 1970 aux laboratoires Bell (ATT : télécoms américains) en vue d écrire le système d exploitation UNIX Permet d écrire efficacement du code de «bas niveau», rapide et proche de la structure de la machine : gestion mémoire, traitement des interruptions, accès aux adresses physiques d E/S
Caractéristiques du langage C 1/ Langage compilé. Code source -> fichiers objets -> programme binaire (<> langage intermédiaire) 2/ Typage très précis des variables 3/ Arithmétique des pointeurs : grande souplesse et efficacité du code mais aussi grand danger de «bugs» 4/ Gestion de la mémoire par le programmeur, pas de «garbage collector»
Typage très précis des variables Le type précis doit être déclaré pour chaque variable avant son utilisation Nom Taille (bits) Utilité Char 8 Caractère short 16 Nombre entier signé int 16, 32 Nombre entier signé Long 32 ou 64 Nombre entier signé Float 32 Nombre flottant (IEEE xxx)double 64 ou 80 ou plus Nombre flottant unsigned char, short, int, long : types entiers non signés (codage des entiers naturels).
Le langage C et les pointeurs C est LE point fondamental du langage C, son grand génie et son grand défaut. Un pointeur est une variable manipulable contenant une adresse mémoire avec en plus la notion de type de l élément pointé. Java a remplacé les pointeurs par des références non directement manipulables.
Les pointeurs en pratique (1) Déclaration char *p1 ; // déclare p1 comme pointeur sur le type caractrère int *p2 ; // déclare p2 comme pointeur sur entier float *p3 ; char **p4 ; /*déclare p4 comme un pointeur sur un tableau de caractères ou comme un pointeur sur des chaines de caractères */ struct article { char name[20];// tableau de 20 carcatères, la place est réservéé de place float prix_unitaire; char code[10]; int id_fournisseur; struct article *p5 : // p5 est un pointeur sur le type structuré article Attention : aucune de ces déclaration ne réserve de place pour stocker des données autre que le pointeur lui-même (= adresse mémoire de l élément pointé.
Les pointeurs en pratique (2) Utilisation char nom[20] = "lurpa" // { l, u, r, p, a, \0 char *p ; p = nom ; /* affectation de p avec l adresse de base du tableau nom*/ printf ("contenu de p = %c", *p); // *p vaut l char c1 = *p++; /* c1 vaut l et p pointe sur nom[1], p est incrémenté après utilisation*/ char c2 = *++p ; /* c2 vaut r, p est incrémenté avant utilisation */ char c3 = *(p+2); /* c3 vaut a, p reste inchangé */ c3 = p[3] ; /* c3 vaut 0, p reste inchangé */ p = p + 30; /* p pointe sur nom[32] (!) */ *p = 0 ; /* accepté par le compilateur */
Les pointeurs en pratique (3) int values[5] = {14,17,205,4,38 ; int *pt = values ; printf ("valeur num %d = %d",2,*(pt+1)) /* affiche valeur num 2 = 17 */ struct article { char name[20]; float prix_unitaire; char code[10]; int id_fournisseur; struct article *pt2; pt2 = get_articles(); // renvoie un pointeur sur une table d articles while (pt2!= NULL) { pt2++ ; /*passe à l article suivant /* déconseillé mais possible */ char *pt3 = (char *)pt2; /*transtypage */ pt3 += sizeof( struct article); /* équivaut à pt2 += 1 */ int prix = (int)*(pt3 + 21); /* affectation de prix par prix_unitaire de l élément pointé par pt3 */
Allocation dynamique et pointeurs Ex 1 : float *T = malloc( 10 * sizeof(float) ); if (T!= NULL) { float *p = T; /* pointeur sur un element du tableau */ int i; for (i=0; i < 10; i++) { *p = i; p = p + 1; /* passe au suivant */ free(t); Ex 2 danger : while( ) { char *p=malloc(nbre); p++;.. free(p);
Gestion mémoire, exemple de code #include <stdio.h> #include <dir.h> #include <conio.h> #include <dos.h> #include <string.h> char *current_directory (char *path) { strcpy (path, "X:\\" ); // on place un X path[0] = 'A' + getdisk(); // on remplace le X par le vrai disque getcurdir (0, path+3); // on recherche les sous-repertoires return (path); void main(void) { /* struct ffblk { char ff_reserved[21]; // reserve par DOS char ff_attrib; // attribut a trouver int ff_ftime; // temps fichier int ff_fdate; // heure fichier long ff_fsize; // taille fichier char ff_name[13]; // nom fichier ; */ struct ffblk ffblk; // info fichiers char curdir[maxpath]; // chemin int done; // compteur current_directory(curdir); done = findfirst("*.*",&ffblk,0); while (!done) { printf(" %s\t", ffblk.ff_name); done = findnext(&ffblk); getch();
De C à C++ Le langage C++ est un sur-ensemble du langage C Les compilateurs C++ compilent les programmes écrits en «ancien» C. (compatibilité ascendante) Objectif : programmer suivant un nouveau paradigme : la Programmation Orientée Objet
C++ et la POO C++ met en œuvre de manière fine tous les concepts de la programmation orientée objet. Mais on peut mélanger la programmation procédurale et la POO.
La Programmation Orientée Objet L idée de la POO est de manipuler des objets en programmation comme on en manipule dans la vie courante. Un objet est constituée d une partie «cachée» qu il n est pas indispensable de connaître pour utiliser l objet (dans une voiture : moteur, boite de vitesse, roues ) et d une partie «publique» sur laquelle intervient l utilisateur (volant, manette de vitesse, bouchon du réservoir, )
POO suite La partie cachée est l implémentation La partie publique est l interface Le fait de cacher l implémentation est désigné par le terme «encapsulation» On définit le type d un objet en définissant une classe qui déclarera des données et des fonctions. Ce sont les membres de la classe qui peuvent être private ou public (mais aussi protected ou static)
Les grands concepts de la POO La classe : regroupement fonctionnel et encapsulation L héritage : spécialisation d un type d objet (càd une classe) par ajout ou modification de membres. L héritage conduit au polymorphisme : on définit des traitements sur des objets génériques sans connaître le type éventuellement plus spécialisé des objets utilisés à l éxécution. La surcharge d opérateur ou de fonction
Déclaration d une classe C++ #ifndef ARTICLE #define ARTICLE #include <string.h> class Article { public: // Constructeur par défaut Article(); // Constructeur de copie Article(const Article& unart); // Constructeur normal Article(const char* nom, float prixbrut, int quantite = 0); // Destructeur virtual ~Article(); // Accès au numéro de l'article. int numero() const {return _numero; // Accès au prochain numéro d'article. static int prochainnumero() const {return _prochainnumero; protected: ; char* _nom; // Nom de l'article float _prixbrut; // Prix brut de l'article int _quantite; // Quantité en stock #endif
Instanciation Les objets réellement créés et utilisés dans un programme à partir d une classe sont des instances de la classe. ils ont une existence en mémoire au moment de l exécution du programme. Ex : Article unart; Article unautre("monarticle",3.5); Article *pt; Article &refart = unart; pt = new Article(nom, prix); /* necessitera une instruction delete pt ; */
Héritage (classe dérivée) Dans l exemple suivant, la classe boisson est une classe dérivée de la classe de base Article class Boisson : public Article { public: // Constructeur Boisson(const char* nom, float prixbrut, int volume, int quantite = 0) : Article(nom, prixbrut, quantite), _volume(volume) { // Accès au volume int volume() const {return _volume; protected: int _volume; // Volume de la boisson (cl) ; Possibilité d héritage multiple : Class Boisson : public Article, public Aliment {... ;
Dérivation et classe virtuelle // Etablissement d une facture : Article *pt_art; float total; pt_art = getarticles(); while (pt_art!= NULL){ total = pt_art->prix_brut(); /* équivalent à total = (*pt_art).prix_brut(); */ pt_art++; Pour une résolution dynamique de la fonction prix_brut() à l exécution, soit l utilisation de la fonction prix_brut() redéfinie par des classes dérivées de Article, il faut déclarer prix_brut comme fonction virtuelle. C est ce qui rend polymorphe l objet Article utilisé dans le code ci-dessus public virtual float prix_brut() {
Surcharge En C++ on peut "surcharger" les opérateurs et les fonctions, dans ou hors des classes. Cela permet d obtenir des comportements d'opérateurs et de fonctions qui dépendent des types et du nombre des arguments. Point operator+ (Point &P1, Point &P2) { Point res(p1.getx()+p2.getx(),p1.gety()+p2.gety(),p1); return res; On peut aussi surcharger << (pour cout) : ostream& operator << (ostream& flux, Point& P) { flux << "[" << P.GetX() << "," << P.GetY() << "]"; return flux; Ces deux surcharges sont globales. Mais on peut également les définir comme fonctions membres : class Point {... Point operator + (Point &P); Point operator = (Point &P); ; Point::Point::operator + (Point & P) { Point r;r.x=this->x+p.x;r.y=this->y+p.y;return r; void main(void) { Point A,B,C; A=B; //appelle A.operator=(B) A=B+C; //appelle B.operator+(C)
Gestion d erreur, les exceptions Utilisation des blocs try catch try { //instructions pouvant lever des exception throw object; catch (argument1) { catch (argument2){ catch( ) { // traitement de toute autre exception
Spécificités diverses Gestion des entrées / sorties : Remplacement de printf() et getch() par les opérateurs << et >> Allocation dynamique New et delete remplacent malloc et free int pi = new int; char *pc = new char[15];.. delete pi; delete [] pc; Templates Containers Itérateurs