Vous êtes sur la page 1sur 32

1 Compléments C++

– Agrégation
– Utilisation de l’héritage
– Compilation séparée
– Espaces de noms
– Vecteurs

1. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


2 Agrégation : relation « A-UN »
2.1 Objets contenant d’autres objets
– On a des classes pour représenter une chaîne de caractères et une date :

class string { class date {


public: public:
string(const char* s); date(int day, int month, int year);
string(const string& s); date(const date& d);
... ...
private: private:
char* d_data; int d_day, d_month, d_year;
int d_size; };
};

2. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– On veut représenter un client : il a un nom et une date de naissance

class client
{
public :
client(const string& name, const date& birthdate);
client(const char* name, int birthday,int birthmonth,int birthyear);
private :
string d_name;
date d_birthdate;
};

– Pour construire un client, il faut d’abord construire d_name, sa partie


string et d_birthdate sa partie date
⇒ on appelle les constructeurs des objets composant un autre objet dans la
liste d’initialisation de son constructeur :

3. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


client::client(const string& name, const date& birthdate) :
d_name{name}, d_birthdate{birthdate}
{
//additional stuff
}

client::client(const char* name,


int birthday, int birthmonth, int birthyear) :
d_name{name}, d_birthdate{birthday,birthmonth,birthyear}
{
//additional stuff
}

– Une fois dans le corps du constructeur, les champs sont déjà construits.

4. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


2.2 Création/destruction des objets composites
Création d’un objet :

1. allocation de la mémoire physique qu’il occupe ;


2. appel des constructeurs des objets qui le composent dans l’ordre de déc-
laration de ces objets : constructeurs de la liste d’initialisation, par défaut
sinon ;
3. exécution du corps de son constructeur.

Destruction d’un objet, en sens inverse :

1. exécution du corps de son destructeur ;


2. appel des destructeurs des objets qui le composent dans l’ordre inverse de
déclaration de ces objets ;
3. libération de la mémoire physique qu’il occupe.

5. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


2.3 Copie des objets composites
Constructeur de recopie
– Le constructeur de recopie généré par défaut crée et initialise les champs
d’un objet un à un par appel à leur constructeur de recopie
⇒ pas besoin de le redéfinir si les constructeurs de recopie des champs sont
corrects

class toto {
A d_a;
B d_b;
C d_c;
};

//generated copy constructor

toto::toto(const toto& t) : d_a{t.d_a}, d_b{t.d_b}, d_c{t.d_c}


{}
Opérateur d’affectation
– L’opérateur d’affectation généré par défaut fait une copie champ à champ
de l’objet, les champs étant copiés avec leur opérateur d’affectation
⇒ pas besoin de le redéfinir si les opérateurs d’affectation des champs sont
corrects

//generated affectation operator

toto& toto::operator=(const toto& t)


{
d_a = t.d_a; //d_a.operator=(t.d_a)
d_b = t.d_b;
d_c = t.d_c;
return *this;
}

7. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


3 Introduction à l’utilisation de l’héritage
– On a besoin de personnaliser une classe :

– en lui ajoutant des nouvelles fonctionnalités,


– mais sans la modifier

→ créer une nouvelle classe dérivée de cette classe (dite classe de base)
– Un objet de la classe dérivée est alors composé d’une partie objet classe de
base
– Le constructeur de la classe dérivée doit appeler celui de la classe de base
– La classe dérivée hérite des méthodes de la classe de base : on peut appeler
les méthodes de la classe de base sur un objet de la classe dérivée
– La classe dérivée est même utilisable partout à la place de la classe de base

8. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– On a une classe point :

class point {
public:
point(double x, double y);
double x() const;
double y() const;
void moveTo(double x, double y);
private:
double d_x, d_y;
};

void utiliserPoint(point& p);

– On veut lui rajouter une étiquette (une chaîne de caractères)


→ créer la classe dérivée pointEtiquete

9. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


class pointEtiquete : public point { //hérite de point
public:
pointEtiquete(double x, double y, const string& etiq);
string etiquette() const;
private:
string d_etiquette;
};

pointEtiquete::pointEtiquete(double x, double y, const string& etiq) :


point{x,y}, //constructeur classe de base
d_etiquette}etiq}
{}

string pointEtiquete::etiquette() const { return d_etiquette; }

– On peut utiliser un pointEtiquete à la place d’un point :


pointEtiquete pe{10,5,"mairie"};
cout<<pe.etiquette()<<" en "<<pe.x()<<", "<<pe.y()<<endl;
utiliserPoint(pe);

10. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


4 Compilation séparée
– Un programme est réparti dans plusieurs fichiers :

– plusieurs personnes travaillent ensembles sur le projet,


– le découpage d’un programme en fichiers aide à exprimer sa structure
logique (pour les programmeurs et le compilateur),
– sans découpage il faudrait tout recompiler au moindre changement.

– Le programme est découpé en plusieurs fichiers source donnés au compi-


lateur qui invoque plusieurs programmes
– Le préprocesseur transforme un fichier source en unité de traduction
– Le compilateur compile une unité de traduction en fichier objet con-
tenant le code objet du fichier source
– L’éditeur de liens rassemble tous les codes objets à l’édition de liens pour
produire le programme exécutable

11. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


4.1 Le C-préprocesseur
– Il est invoqué automatiquement en premier.
– Il fait des transformations de texte à partir du fichier source grâce à des
directives avant de transmettre le résultat au compilateur.

Inclusion de fichiers :

– La directive #include permet d’inclure textuellement le contenu d’un


autre fichier

#include<iostream> //standard C++ header files


#include<time.h> //other system header files
#include"myheader.h" //user header files

12. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


Macros :

– La directive #define permet définir des macros qui définissent des sub-
stitutions automatiques dans le texte :

#define NMAX 10
//NMAX is textually replaced by 10

#define max(a,b) (a>b) ? a : b


//macro with parameters

– Les macros sont dangereuses : pas de vérification de types et effets de bord


⇒ à proscrire !
– Utiliser les constantes et les fonctions en-ligne à la place.

13. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– On peut tester si une macro a déjà été définie ou non avec les directives
#ifdef ... #endif et #ifndef ... #endif
– Un compilateur C++ définit toujours la macro __cplusplus,
– On peut définir une macro dans un fichier puis la tester ailleurs pour savoir
si le fichier a déjà été inclus : permet d’éviter les inclusions multiples

⇒ à utiliser systématiquement pour les fichiers en-tête.

fichier myinclude.h :

#ifndef MYINCLUDE_H
#define MYINCLUDE_H

//contenu du fichier en-tête myinclude.h

#endif

14. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


4.2 Compilation en fichier objet
– Pour pouvoir compiler une unité de traduction en un fichier objet, le com-
pilateur doit connaître :

– les déclarations des fonctions appelées (définitions si en-ligne),


– les définitions des types utilisateurs utilisés ;

– Les définitions des types utilisateurs ne peuvent apparaître qu’une seule


fois dans une unité de traduction ;
– L’unité de traduction doit contenir les définitions de tout ce qui ne peut
être partagé avec d’autres unités.

15. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


4.3 Édition de liens
– L’éditeur de liens regroupe en un seul tout exécutable les codes objets qui
lui sont donnés
– Il remplace les noms des fonctions et des variables (symboles) par leur
adresse mémoire dans le code final
→ les définitions des variables globales et fonctions utilisées doivent figurer
au final une et une seule fois,
– Les définitions des types utilisateurs peuvent apparaître plusieurs fois au
final si c’est de façon identique.

16. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


4.4 Découpage en fichiers
→ On répartit le code dans deux sortes de fichiers :

– les fichiers en-tête :

– ils sont inclus par les autres fichiers pour pouvoir utiliser les types
(classes, . . . ), fonctions . . . , qui y sont déclarés,
– ils doivent être protégés contre les inclusions multiples,

– les fichiers source :

– ils contiennent les définitions,


– ce sont eux qui sont passés au compilateur pour créer les fichiers
objets.

17. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– Un fichier en-tête peut contenir :

des inclusions de fichiers en-tête #include<cmath>


des commentaires documentants //author: .....

des définitions de constantes const double PI=3.1415926535;


des définitions d’énumérations enum light { red, yellow, green };

des déclarations de noms class list;


des définitions de types class point {
double x,y;
public:
point(double x,double y);
};

des déclarations de fonctions void print(point );


des définitions de fonctions en-ligne inline double dist(point p)
{ return sqrt(p.x*p.x+p.y*p.y); }

18. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– Un fichier source peut contenir :

des inclusions de fichiers en-tête #include"point.h"

les définitions des méthodes void point::moveTo(double x,double y)


{ d_x=x; d_y=y; }

les définitions des fonctions void print(point p)


{ cout<<’(’<<p.x()<<’,’<<p.y()<<’)’; }

19. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


5 Gestion de la compilation séparée : make et
Makefile
– Pour avoir un programme exécutable il faut :

– compiler les fichiers source en fichiers objets :


g++ -c toto.cpp
g++ -c tata.cpp
...
– réunir les fichiers objets à l’édition de liens :
g++ toto.o tata.o ... -o resultat

– Quand des fichiers sont modifiés, il ne faut recompiler que ces fichiers et
ceux qui les incluent
⇒ gestion automatique des dépendances grâce au programme make et à son
fichier de configuration Makefile.

20. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


5.1 Makefile
– Un fichier de configuration Makefile contient des instructions de la forme :

cible : dépendances
<tab>commande à exécuter

– cible : nom d’un fichier à générer


– dépendances : noms des fichiers qui s’ils sont modifiés entraînent la
re-génération de la cible
– commande à exécuter : commande à exécuter pour générer la cible

– Le programme make lit ce fichier Makefile pour déterminer quelles sont


les commandes à exécuter pour générer les fichiers :

– make cible : génère la cible indiquée


– make : génère la première cible du Makefile

21. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– Pour générer une cible make :

– génère les dépendances si ce sont des cibles


– re-génère la cible en exécutant la commande si
la cible n’est pas à jour, c.-à-d. si elle n’existe
pas ou est plus vieille qu’une des dépendances.

– Gestion de la compilation séparée :

– compiler les fichiers sources en fichiers objets


– édition de liens pour générer l’exécutable

#génération du programme exécutable


progtest : toto.o tata.o progtest.o
<tab>g++ toto.o tata.o progtest.o -o progtest

#génération des codes objets


progtest.o : progtest.cpp toto.h tata.h
<tab>g++ -c progtest.cpp

toto.o : toto.cpp toto.h


<tab>g++ -c toto.cpp

tata.o : tata.cpp tata.h


<tab>g++ -c tata.cpp

22. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– Une cible ne désigne pas forcément un fichier à
créer mais peut désigner seulement des ordres à
effectuer

#faire le nettoyage complet : make clean


clean :
<tab>rm *.o
<tab>rm progtest

– Variables :
on peut définir des variables dans un Makefile
pour simplifier l’écriture ou les changements :

– définition d’une variable : NOMVAR = valeur


– utilisation d’une variable : $(NOMVAR)

#nom du compilateur et options de compilation


CXX = g++
CXXFLAGS = -g -Wall

progtest : toto.o tata.o progtest.o


<tab>$(CXX) toto.o tata.o progtest.o -o progtest

#génération des codes objets


progtest.o : progtest.cpp toto.h tata.h
<tab>$(CXX) $(CXXFLAGS) -c progtest.cpp

toto.o : toto.cpp toto.h


<tab>$(CXX) $(CXXFLAGS) -c toto.cpp

23. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


6 Les espaces de noms
– On veut rassembler dans une même portée de noms
plusieurs composants formant un tout logique
– Pour que les noms de ces composants ne soient
pas en conflits avec ceux d’autres bibliothèques
→ Déclarer des espaces de noms namespace :
namespace myGraphics
{
class window { ... };
int nbOpenWindows;
void error(const char* );
}

namespace myDatastr
{
class vector { ... };
bool is_sorted(const vector& );
void error(const char* );
}

– Un espace de noms introduit une portée


⇒ les composants sont accessibles par leur nom qua-
lifié, c.-à-d. préfixé par nom_du_namespace::
– Les composants de la bibliothèque standard sont
dans l’espace de noms std

24. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


#include<cmath>
#include"myGraphics.h"
#include"myDatastr.h"

int main()
{
myGraphics::window w;
if (!w.open())
{ myGraphics::error("cannot open window"); }

myDatastr::vector v(1000);
for (int i=0; i<1000; ++i) { v[i] = std::sqrt(i); }
if (!myDatastr::is_sorted(v))
{ myDatastr::error("error with is_sorted"); }

return 0;
}

25. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


– Les définitions des composants sont données à l’intérieur de la définition
d’un espace de noms :
#include"datastr.h"

namespace myDatastr
{
vector::vector(int size)
{ ... }

bool is_sorted(const vector& v)


{ ... }

void error(const char* msg)


{ ... }
}

26. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


On peut rendre des composants directement ac-
cessibles grâce à :
– une déclaration d’utilisation pour un composant ;
– une directive d’utilisation pour tous les composants
d’un espace de nom (déconseillé).

#include"myGraphics.h"
#include"myDatastr.h"

using myDatastr::vector; //using declaration


using namespace myGraphics; //using directive

int main()
{
window w;
if (!w.open())
{ error("cannot open window"); }

vector v(1000);
for (int i=0;i<1000;++i) { v[i]=std::sqrt(i); }
if (!myDatastr::is_sorted(v))
{ myDatastr::error("error with is_sorted"); }
}

27. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


7 Outils de base de la STL : conteneurs
7.1 Principes des conteneurs
– Le type des conteneur est paramétré par le type des éléments qu’il con-
tient : conteneur<typeelem> c;
– Les conteneurs ont une sémantique par valeur : ils copient les éléments
insérés en interne et renvoient des copies des éléments
– Les éléments d’un conteneurs doivent pouvoir

– être copiables grâce au constructeur de recopie,


– être affectables grâce à l’opérateur d’affectation,
– être destructibles grâce à leur destructeur,

– Pour certaines opérations, les éléments doivent avoir un constructeur par


défaut, un opérateur d’égalité ==, être comparables (opérateur <)

28. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


Constructeurs et affectation
ContType c crée un conteneur vide
ContType c(c2) constructeur de recopie
ContType c(n) n éléments construits par défaut
ContType c(n,val) n copies de val
˜ContType() détruit le conteneur et ses éléments
c1=c2 affecte les éléments de c2 à c1
c1.swap(c2) échange les données de c1 et c2

– Les opérations ne sont pas sécurisées : l’utilisateur doit faire attention à la


validité des paramètres.

29. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


Informations et accès aux éléments
c.size() nombre d’éléments
c.empty() est-ce-que le conteneur est vide ?
c.max_size() nombre maximum possible d’éléments
c1==c2 est-ce-que les contenus sont les mêmes ?
c1!=c2 est-ce-que les contenus sont différents ?
c1<c2 est-ce-que c1 est avant c2 (ordre lexicographique)
c.front() premier élément
c.back() dernier élément
c[i] accès non vérifié (vector, indice commence à 0)
c.at(i) accès vérifié (lance l’exception out_of_range)
Insertion/suppression
c.push_back(elem) insère l’élément à la fin
c.pop_back() enlève le dernier élément
c.push_front(elem) insère l’élément au début (list)
c.pop_front() enlève le premier élément (list)
c.clear() Enlève tous les éléments (conteneur vide)

30. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


7.2 Vecteurs
– Pour l’utiliser :

– #include<vector>
– vector<typeelem> v

– Un vecteur représente un tableau dynamique


– Accès aléatoire aux éléments : v[i]
– Pour ajouter ou enlever un élément : le faire à la fin du vecteur avec
push_back (insérer) et pop_back (enlever)
(l’insertion/suppression ailleurs n’est pas performante pour les grands vecteurs).

31. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière


Taille et capacité
– La taille (=nombre d’éléments) peut être changée avec resize(n) ou
resize(n,val) (éléments supplémentaires initialisés avec val)
– Un vecteur alloue plus de mémoire que nécessaire
– Capacité : nombre d’éléments que le vecteur peut contenir sans avoir à
faire de ré-allocation (capacity())
– La capacité peut être fixée par l’utilisateur avec reserve()

std::vector<int> v;
v.reserve(80);

ou à la construction (dans ce cas taille=capacité)

std::vector<int> v(80); //initialized with default constructed values

– Pour diminuer la capacité à la taille du vecteur : v.shrink_to_fit()

32. Prog événementielle et interfaces graphiques – L2 INFO Stéphane Rivière