Académique Documents
Professionnel Documents
Culture Documents
Licence 3 Informatique
David Genest
Année universitaire 2018-2019
Chapitre 1
Introduction
Présentation du cours
De C à C++
Présentation du cours
Présentation du cours
De C à C++
Programmation orientée objet C++
Objectif du cours
▶ Approfondir les concepts de la programmation orientée objet.
(Déjà vus en Java dans le cours de POO)
▶ Apprendre le langage C++ (moderne).
▶ Utiliser des outils d’aide au développement.
Environnement de développement, débogueur…
Organisation
▶ 20h de cours : Concepts de la POO et C++.
▶ 44h de TP : Programmation C++ et outils de développement.
▶ + Bases de la programmation d’interfaces graphiques (Qt).
Évaluation
▶ 1 2 1
3 CC + 3 Examen. CC : 2 TP relevés et notés.
Introduction POO
Langage à classes
La plupart des langages de programmation OO sont des langages à
classes : une classe définit un « moule » pour un ensemble d’objets
qui ont la même structure et fournissent les mêmes traitements.
3
Introduction C++
C++
▶ Langage normalisé par l’ISO
▶ Défini dans les années 1980 (mais a évolué depuis : C++98, C++03,
C++11, C++14, C++17)
▶ « Amélioration » de C ⇒ facilite l’apprentissage pour quelqu’un
qui connaît déjà C. Mais … faire du C en C++ n’est pas programmer
en C++ ! Réutilisation facilitée de code/bibliothèques C
▶ Doté d’une bibliothèque de classes et algorithmes
▶ Portable
Présentation du cours
De C à C++
Types et variables
Allocation dynamique
const
Références
Inférence de type
Conversion de type
Compilation
De C à C++
Types et variables
Types de données
▶ signed ; unsigned
▶ char ; short (short int); int ; long (long int); long
long (long long int)
float ; double ; long double
▶ wchar_t (unicode)
▶ bool → true false
6
Types de données
#include <iostream>
int main() {
int a,b;
std::cin >> a;
std::cin >> b;
std::cout << f(a,b) << std::endl;
return 0;
}
7
Portée des variables
8
Portée des variables
#include <iostream>
signed short varglobale;
int main() {
varglobale=5;
unsigned int varlocale=10; // initialisation
while (varglobale >= 0) {
int v(6);
varglobale += v;
varlocale--;
}
std::cout << varglobale << "\n";
return 0;
}
9
Déclaration et initialisation des variables
Multiple déclarations
int a, b, c; // équivalent à
int a; int b; int c;
Attention
int* a, b;
Dans les deux cas, il s’agit d’une initialisation (et non une
affectation).
Exemple
int t1[4];
int t2[4]={1,5,7,9};
int t3[]={12,42,23};
int t2d[4][2];
t2d[2][1] = 12;
t2d[4][2] = 23; // Attention !
Attention
Un tableau est passé par pointeur à une fonction.
Une fonction ne peut pas connaître la taille d’un tableau.
int maximum(int tab[], int taille) { ... }
std::cout << maximum(t1,4);
void fnct(int tab[]) { tab[1]=5; }
fnct(t1); std::cout << t1[1];
http://en.cppreference.com/w/cpp/container/array
13
Tableaux C++ std::array
array (array.cc)
#include <iostream>
#include <array>
int max(std::array<int,5> t) {
int m(t[0]);
for (std::size_t i=1; i<5; ++i)
if (t[i]>m)
m=t[i];
return m;
}
int main() {
std::array<int,5> tab {3,7,1,2,6};
std::cout << max(tab) << "\n";
std::array<int,5> tab2(tab);
tab2[2]=12;
tab2.at(5)=15; // Erreur !
tab=tab2;
return 0;
} 14
Tableaux C++ std::array
15
Boucle for d’ intervalle
16
Boucle for d’ intervalle
#include <iostream>
#include <array>
void afficher(std::array<int,5> t) {
for (int a : t)
std::cout << a << "␣";
std::cout << "\n";
}
int main() {
std::array<int,5> tab {3,7,1,2,6};
for (int a : {3,7,1,2,6})
std::cout << a << "\n";
for (int a : tab)
a *= 2; // Attention , ne f a i t rien !
afficher(tab);
return 0;
} 17
struct et enum
18
struct et enum
#include <string>
typedef unsigned int Annee;
enum EtatPersonne {
MAJEUR,
MINEUR
};
struct Personne {
std::string nom;
std::string prenom;
EtatPersonne etat;
Annee naissance;
};
int main() {
Personne p; // et non struct Personne p
p.nom = "Durand"; p.etat = MINEUR;
Personne p2 { "Dupont", "Joe", MAJEUR, 1990 };
return 0;
} 19
using
using peut être utilisé à la place de typedef avec une syntaxe plus
naturelle.
Exemple
using Annee = unsigned int;
using Tableau = std::array<std::string, 10>;
20
enum
21
Énumérations fortement typées : enum class
22
Arguments par défaut
Les derniers arguments d’une fonction peuvent avoir une valeur par
défaut lors de la déclaration de la fonction.
Si les arguments correspondants ne sont pas passés lors d’un appel,
la valeur par défaut est prise en compte.
Exemple
void f(int a=2, int b=3) {
std::cout << a << "␣" << b << "\n";
}
...
f(); // 2 3
f(5); // 5 3
f(5,7); // 5 7
23
De C à C++
Allocation dynamique
Allocation dynamique en C
Syntaxe en C
▶ Allocation simple
int * i = (int *)(malloc(sizeof(int)));
▶ Allocation d’une zone
int * t = (int *)(malloc(sizeof(int)*taille));
▶ Libération
free(t);
24
Allocation dynamique en C++
Syntaxe en C++
▶ Allocation simple
int * i = new int;
int * i = new int(3); Fixer la valeur initiale
▶ Allocation d’une zone
int * t = new int[taille];
▶ Libération simple
delete i;
▶ Libération d’une zone
delete []i;
25
Allocation dynamique en C++
Attention
▶ Tout ce qui est alloué doit être libéré.
▶ Tout ce qui est alloué par new ...[] doit être libéré par
delete [].
▶ Ne pas mélanger new/delete avec malloc/free.
26
De C à C++
const
const
i = 5; // erreur de compilation
Une (variable) constante (mais pas une variable (non déclarée const))
peut être utilisée pour spécifier la taille d’un tableau.
Souvent on marque syntaxiquement les constantes (écriture en
majuscules, première lettre en majuscule, préfixe particulier tel que
c_ …)
27
const et pointeurs
Références
Références
Une référence est une variable synonyme (qui porte le même nom)
qu’une autre variable.
Syntaxe (Déclaration d’une référence)
type & nomvar(var_référencée);
i=5;
std::cout << j; // 5
j=3;
std::cout << i; // 3
29
Références
30
Passage par valeur / référence
31
Passage par valeur / référence
32
Passage par valeur / référence
#include <iostream>
#include <array>
#include <algorithm>
const int Tailletab(10000);
using tableau=std::array<int, Tailletab>;
// int maximum( tableau t , std : :size_t n=Tailletab −1) {
// provoque une erreur de segmentation
int maximum(tableau const & t, std::size_t n=Tailletab-1) {
if (n == 0)
return t[n];
else
return std::max(maximum(t, n-1), t[n]);
}
int main() {
tableau t;
for (int & init : t) init = random() % 1000;
std::cout << maximum(t) << "\n";
return 0; 33
De C à C++
Inférence de type
Inférence de type : auto
Exemple
auto f; //declaration of ’auto f ’ has no i n i t i a l i z e r
std::array<int,10> tab { ....};
auto premier(tab[0]);
premier = "ch"; //invalid conversion from ’ const char * ’ to ’ int ’
auto t2(tab);
auto max(maximum(tab));
34
auto
using tableau=std::array<int,5>;
35
auto
auto (auto.cc)
37
auto
auto (auto.cc)
38
De C à C++
Conversion de type
Conversion de type
Syntaxe en C
(nouveautype)expression
Exemple
int main() {
int a(2), b(3);
float f(a/b);
std::cout << f;
return 0;
}
▶ Affiche 0
▶ Forcer la division sur les réels
float f(((float)a)/b);
▶ Affiche 0.666667
39
Conversion de type
Compilation
Compilation
41
Compilation avec CMake
42
Compilation avec CMake
1 cmake_minimum_required(VERSION 3.1.0)
2 project(nomprojet CXX)
3
4 set(CMAKE_CXX_STANDARD 14)
5 set(CMAKE_CXX_STANDARD_REQUIRED on)
6 set(CMAKE_CXX_EXTENSIONS off)
7
43
Compilation avec CMake
44
Compilation avec CMake
45
Chapitre 2
Classes : Concepts de base
Introduction
Syntaxe
Encapsulation
Constructeur
Destructeur
Méthodes constantes
Introduction
Introduction
Syntaxe
Encapsulation
Constructeur
Destructeur
Méthodes constantes
Classe
46
Classe
47
Classe
48
Syntaxe
Introduction
Syntaxe
Encapsulation
Constructeur
Destructeur
Méthodes constantes
Classe
Syntaxe Déclaration
class NomClasse {
typemembre nomattribut;
typeretour nommethode(typearg1 nomarg1, ...);
...
};
49
Classe
#pragma once
#include <string>
class Fichier {
public:
void afficher();
unsigned int taille() {
return _taille; }
std::string nom() {
return _nom; }
void renommer(std::string const & nn);
void fixerTaille(unsigned int nt);
private:
std::string _nom;
unsigned int _taille;
};
50
Classe
#include "fichier1.hh"
#include <iostream>
void Fichier::afficher() {
std::cout << this->_nom << "␣(";
if (_taille == 0)
std::cout << "vide)";
else
std::cout << _taille << "␣octets)";
}
C++ n’est pas Java : On préfèrera quand c’est possible manipuler les
objets par valeur (et non par pointeur).
52
Classe - Accès aux membres
53
Classe
54
Classe
#include "fichier1.hh"
int main() {
Fichier f;
f.renommer("exemple.txt");
f.fixerTaille(2567);
f.afficher();
return 0;
}
55
Encapsulation
Introduction
Syntaxe
Encapsulation
Constructeur
Destructeur
Méthodes constantes
Encapsulation
56
Encapsulation
57
Encapsulation
Tous les membres qui sont déclarés après une étiquette de visibilité
ont cette visibilité là. Les membres qui sont déclarés avant la
première étiquette de visibilité sont privés.
Exemple
_nom et _taille sont privés, les 5 méthodes sont publiques.
58
Encapsulation
Exemple
Fichier::nom() est un accesseur, Fichier::fixerTaille() est
un mutateur.
59
Constructeur
Introduction
Syntaxe
Encapsulation
Constructeur
Présentation
Liste d’initialisations
Constructeur par défaut et par recopie
Destructeur
Méthodes constantes
Constructeur
Présentation
Constructeur
Exemple
Fichier f;
f.renommer("exemple.txt");
f.afficher(); // f . _taille n ’ est pas i n i t i a l i s é .
60
Constructeur
61
Constructeur
Syntaxe Constructeurs
class NomClasse {
...
// Constructeur par défaut .
NomClasse();
// Constructeur avec paramètres .
NomClasse(typearg1 nomarg1, ...);
// Constructeur par recopie .
NomClasse(NomClasse const & nomarg);
...
};
62
Constructeur
class Fichier {
public:
Fichier(std::string const & nom, unsigned int taille);
void afficher();
63
Constructeur
Liste d’ initialisations
Constructeur - Liste d’ initialisations
64
Constructeur - Liste d’ initialisations
Différences entre :
Fichier::Fichier(std::string const & nom, unsigned int taille) {
_nom = nom;
_taille = taille;
}
et
Fichier::Fichier(std::string const & nom, unsigned int taille)
:_nom(nom),_taille(taille) {
}
67
Constructeur - Constructeur délégué
#pragma once
#include <string>
struct fichierexterne { // struct externe à notre projet
char const * nom;
long taille;
};
class Fichier {
public:
Fichier(std::string const & nom, unsigned int taille)
:_nom(nom), _taille(taille) {}
Fichier(fichierexterne const & info)
:Fichier(info.nom, static_cast<unsigned int>(info.taille)) {}
private:
std::string _nom;
unsigned int _taille;
};
68
Constructeur - Constructeur délégué
#include "fichierconstrdelegue.hh"
int main() {
fichierexterne fe {".bashrc", 742};
Fichier f(fe);
return 0;
}
69
Constructeur - Liste d’ initialisations
70
Constructeur
Si aucun constructeur n’est déclaré dans une classe, elle dispose tout
de même d’un constructeur par défaut qui :
▶ Appelle le constructeur par défaut sur tous les attributs de types
classes.
▶ Ne fait rien pour les attributs de types primitifs.
Si (au moins) un constructeur a été déclaré dans une classe, le
constructeur par défaut implicite n’est plus disponible. Mais un
constructeur par défaut peut être déclaré.
Constructeur et usage (mainfichier.cc)
int main() {
// Fichier f ; // Provoque une erreur
Fichier f1("ex", 25);
f1.renommer("exemple.txt");
f1.fixertaille(2567);
71
f1.afficher();
Constructeur par recopie
72
Constructeur par recopie
…
▶ Passer une copie d’une variable comme paramètre à une fonction
ou méthode.
Passage par valeur (fichier.cc)
bool Fichier::estpluspetitque(Fichier f) {
return _taille < f._taille;
}
Fichier f2(f1);
std::cout << f1.estpluspetitque(f2) << "\n";
74
Destructeur
Introduction
Syntaxe
Encapsulation
Constructeur
Destructeur
Méthodes constantes
Destructeur
▶ Fermer des fichiers qui ont été ouverts dans les méthodes de la
classe.
▶ Fermer une connexion réseau.
▶ Libérer les allocations dynamiques.
75
Destructeur
76
Destructeur
#pragma once
#include <string>
using fichierproprietaire = unsigned int;
using fichierdate = unsigned int;
using fichiernom = std::string;
using fichiertaille = unsigned int;
struct infofichier {
fichierproprietaire _proprietaire;
fichierdate _creation;
};
77
Destructeur
class fichier {
public:
fichier(fichiernom const & nom, fichiertaille taille);
fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation);
void afficher();
fichiertaille taille() {
return _taille; }
fichierdate creation();
bool estpluspetitque(fichier f);
private:
fichiernom _nom;
fichiertaille _taille;
infofichier * _infosuppl;
};
78
Destructeur
#include "fichier2.hh"
#include <iostream>
void fichier::afficher() {
std::cout << _nom << "␣(";
std::cout << _taille << "␣octets)";
if (_infosuppl)
std::cout << "␣création␣" << creation(); 79
}
Destructeur
fichierdate fichier::creation() {
if (_infosuppl)
return _infosuppl->_creation;
else
return 0;
}
bool fichier::estpluspetitque(fichier f) {
return _taille < f._taille;
}
80
Destructeur
Problème
Quand une instance de fichier cesse d’exister, l’infofichier
associé continue d’exister.
81
Destructeur
destructeur (fichier2bis.hh)
class fichier {
public:
fichier(fichiernom const & nom, fichiertaille taille);
fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation);
~fichier();
destructeur (fichier2bis.cc)
fichier::~fichier() {
delete _infosuppl;
}
class fichier {
public:
fichier(fichiernom const & nom, fichiertaille taille);
fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation);
fichier(fichier const & f);
~fichier();
Règle des 3
Quand dans une classe, on écrit l’une de ces méthodes, on écrit les
3:
85
Constructeur par recopie et destructeur
Ce qui ne veut pas dire qu’il n’y a pas de constructeur par recopie ou
de destructeur !
Mais que c’est le comportement par défaut du constructeur par
recopie/destructeur qui est attendu. Il est possible de l’écrire
explicitement :
Exemple
class C {
public:
C(C const & c)=default;
~C()=default; 86
...
Méthodes constantes
Introduction
Syntaxe
Encapsulation
Constructeur
Destructeur
Méthodes constantes
Méthodes const
const_cast
Méthodes constantes
Méthodes const
Méthodes const
Définition
Quand une méthode est déclarée constante :
▶ Elle ne peut pas modifier les valeurs des attributs de l’objet sur
lequel elle s’applique.
▶ Elle ne peut pas appeler sur l’objet courant une méthode qui
n’est pas constante.
▶ Elle peut être appelée sur un objet constant (contrairement à une
méthode non constante).
87
Méthodes const
#pragma once
#include <string>
class personne {
public:
personne(std::string const & nom, unsigned int age)
:_nom(nom), _age(age) {}
std::string const & nom() const;
unsigned int age(); // n ’ est pas const
bool estplusjeuneque(personne const & p2) const;
private:
std::string _nom;
unsigned int _age;
};
88
Méthodes const
Exemple
std::string const & personne::nom() const {
// _nom = ” test ” ; // erreur
// std : :cout << age( ) ; // erreur
// std : :cout << _age ; // OK
return _nom;
}
89
Méthodes const
Il est autorisé d’appeler une méthode const sur un objet sur lequel
les modifications sont autorisées.
Exemple
unsigned int personne::age() {
// _nom = ” test ” ; // OK
// std : :cout << nom( ) ; // OK
return _age;
}
Avantage
Le compilateur vérifie à la compilation que l’objet n’est pas modifié.
Problème
Si le code utilisé n’emploie pas toujours const là où il le devrait,
certaines méthodes ne peuvent pas être appelées…
const_cast
const_cast
92
const_cast
Exemple
bool personne::estplusjeuneque(personne const & p2) const {
return const_cast<personne *>(this)->age() < const_cast<personne
&>(p2).age();
}
Il n’y a pas de raison d’écrire cela : age() doit être déclarée const.
93
Chapitre 3
Héritage
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Héritage
Intérêt : Réutilisation
Utiliser une classe existante…
95
Syntaxe
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Héritage - Syntaxe
Syntaxe héritage
class NomClasseFille:
public NomClasseMere1, public NomClasseMere2 ... {
// Déclaration des nouveaux attributs
// Déclaration des nouvelles méthodes
// Redéfinition de méthodes
...
};
96
Héritage - Intérêts
Intérêts de l’héritage :
▶ Favoriser la réutilisation.
▶ Factoriser le code.
▶ Catégoriser les concepts dans une relation de généralisation
Chaque concept est caractérisé par ses différences par rapport à
son (ses) parent(s).
97
Exemple
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Héritage - Exemple
99
Héritage
#pragma once
#include <string>
using reference=unsigned int;
using prix=float;
class produit {
public:
produit(reference ref, std::string const & nom, prix prixht);
std::string const & nom() const;
prix prixttc() const;
void afficher() const;
// + accesseurs prixht , référence
private:
reference _ref;
std::string _nom;
prix _prixht;
};
100
Héritage
#include "produitv1.hh"
#include <iostream>
produit::produit(reference ref, std::string const & nom, prix prixht
)
:_ref(ref), _nom(nom), _prixht(prixht) {
}
std::string const & produit::nom() const {
return _nom;
}
float produit::prixttc() const {
return _prixht * 1.20;
}
void produit::afficher() const {
std::cout << _ref << "␣" << _nom << "\n";
std::cout << "prix␣HT␣:␣" << _prixht << "\n";
std::cout << "prix␣TTC␣:␣" << prixttc() << "\n";
}
101
Héritage
#pragma once
#include "produitv1.hh"
using date=std::string;
class produitperissable: public produit {
public:
produitperissable(reference ref, std::string const & nom, prix
prixht, date const & peremption);
date const & peremption() const {
return _peremption; }
void afficher() const;
private:
date _peremption;
};
102
Héritage
#include "produitperissablev1.hh"
#include <iostream>
103
Héritage
Test (produittestv1.cc)
#include "produitv1.hh"
#include "produitperissablev1.hh"
#include <iostream>
int main() {
produit p(1, "test", 100);
// Ceci provoque une erreur :
// produit p ;
// Ceci provoque une erreur :
// produit p=new produit ( 1 , ” test ” , 100) ;
// Ceci est inutilement compliqué :
// produit * p = new produit ( 1 , ” test ” , 100) ;
104
Héritage
p.afficher();
produitperissable p2(2, "test2", 100, "15/10");
std::cout << p2.nom() << "\n";
p2.afficher();
return 0;
}
105
Constructeurs et destructeurs
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Héritage et constructeurs
Constructeur (produitperissablev1.cc)
106
Héritage et destructeurs
107
Redéfinition de méthodes
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Redéfinition
▶ Quand une méthode est redéfinie dans la classe fille, elle doit
être déclarée avec la même signature que dans la classe mère.
▶ Le code d’une méthode redéfinie fait souvent appel à la méthode
de la classe mère (super méthode)
NomDeLaClasseMere::NomDeLaMethode(arguments)
108
Liaison dynamique / liaison statique
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Présentation du problème
▶ Quel que soit le produit, le prix TTC est toujours obtenu par
_prixht * (1 + taux_de_tva)
▶ Ce qui change est le taux de TVA, pas le calcul du prix TTC
⇒ La méthode prixttc sera définie dans produit et ne sera pas
redéfinie dans les sous-classes.
▶ Cette méthode a besoin du taux de TVA appliqué au produit (qui,
lui, est spécifique au produit).
109
Une première (mauvaise) solution
Défauts
▶ Nécessité de fournir le taux de TVA à chaque création de produit
▶ Chaque instance de produit contient une valeur « différente »
▶ Que faire si le taux de TVA est mis à jour ?
Le taux de TVA n’est pas une information associée à un(e instance de)
produit mais à une classe (ensemble d’ instances) de produits. 110
Une meilleure solution
class produit {
public:
produit(reference ref, std::string const & nom, prix prixht);
std::string const & nom() const;
float tauxtva() const;
prix prixttc() const;
#pragma once
#include "produitv2.hh"
class produitculturel: public produit {
public:
produitculturel(reference ref, std::string const & nom, prix
prixht);
float tauxtva() const;
};
#include "produitculturelv2.hh"
produitculturel::produitculturel(reference ref, std::string const &
nom, prix prixht)
:produit(ref, nom, prixht) {
}
float produitculturel::tauxtva() const {
return 0.055; 112
}
Une meilleure solution… vraiment ?
#include "produitperissablev2.hh"
#include "produitculturelv2.hh"
#include <iostream>
int main() {
produitperissable pp(1, "yaourt", 100, "15/10");
produitculturel pc(2, "livre", 100);
std::cout << pp.tauxtva() << "␣";
std::cout << pc.tauxtva() << "\n";
std::cout << pp.prixttc() << "␣";
std::cout << pc.prixttc() << "\n";
return 0;
}
0.2 0.055
120 120 113
Liaison statique
Liaison statique
Quand une méthode à liaison statique est appelée sur un objet,
c’est la méthode correspondant à la classe de cet objet déterminée
au moment de la compilation qui est exécutée.
114
Liaison statique
▶ Quand prixttc est exécuté sur pp comme sur pc, c’est le code de
la méthode de produit qui est exécuté.
Quand la ligne _prixht + (tauxtva()+ 1); est exécutée,
l’objet courant (this) est un (pointeur sur un) produit.
tauxtva étant à liaison statique, c’est le code de
produit::tauxtva qui est exécuté.
▶ Dans le cas de pp, ce n’est pas gênant car tauxtva n’est pas
redéfinie dans produitperissable
▶ Dans le cas de pc, la redéfinition de tauxtva est ignorée, comme
si pc n’était qu’un produit
115
Liaison dynamique
Liaison dynamique
Quand une méthode à liaison dynamique est appelée sur un objet,
c’est la méthode correspondant à la classe « réelle » de cet objet
déterminée au moment de l’exécution qui est exécutée.
116
Liaison dynamique
class produit {
public:
produit(reference ref, std::string const & nom, prix prixht);
std::string const & nom() const;
virtual float tauxtva() const;
prix prixttc() const;
117
Liaison dynamique
118
Liaison dynamique
Résultat affiché :
0.2 0.055
120 105.5
119
Liaison dynamique
120
Liaison dynamique
121
Classes abstraites
Syntaxe
Exemple
Constructeurs et destructeurs
Redéfinition de méthodes
Liaison dynamique / liaison statique
Classes abstraites
Présentation du problème
122
Une solution (qui ne fonctionne pas)
123
Pourquoi cette erreur ?
124
Méthode virtuelle pure (ou Méthode abstraite)
Syntaxe Déclaration
virtual typeretour nommethode(arguments)=0;
125
Méthode virtuelle pure
class produit {
public:
produit(reference ref, std::string const & nom, prix prixht);
std::string const & nom() const;
virtual float tauxtva() const =0;
126
Méthode virtuelle pure
#pragma once
#include "produitv4.hh"
class produitcourant: public produit {
public:
produitcourant(reference ref, std::string const & nom, prix prixht
);
float tauxtva() const override;
};
#include "produitcourantv4.hh"
produitcourant::produitcourant(reference ref, std::string const &
nom, prix prixht)
:produit(ref, nom, prixht) {
}
float produitcourant::tauxtva() const {
return 0.2; 128
}
Méthode virtuelle pure
129
Classe abstraite
Classe abstraite
▶ Toute classe contenant (au moins) une méthode virtuelle pure est
appelée abstraite.
▶ Une classe abstraite ne peut être instanciée.
Exemple
produit p; // provoque une erreur
produitcourant p1; // ok
produit * pp; // ok
produit * pp1 = new produitcourant(...); // ok
Attention
Si une classe B est classe fille de A, si dans A est déclarée une
méthode m virtuelle pure, et si B ne redéfinit pas m…
alors B contient une méthode virtuelle pure, elle est donc abstraite.130
Classe abstraite
Dans l’exemple,
131
Chapitre 4
Classes : Compléments
132
Attributs de classe - Une solution
▶ Utiliser un compteur
▶ initialisé à 0 au début de l’exécution du programme ;
▶ incrémenté à chaque instanciation de produit.
▶ Ce compteur est utilisé par le constructeur de produit pour
récupérer une valeur unique afin d’ initialiser _ref.
▶ Ce compteur devrait être « caché » afin d’être visible des seules
méthodes de produit.
En faire un attribut private de produit ?
133
Attributs de classe - Une solution qui ne fonctionne pas
Code incorrect
Chaque instance de produit dispose d’une valeur de l’attribut
_compteur… alors qu’un compteur partagé (par toutes les instances)
est requis.
134
Attributs de classe
135
Attributs et méthodes de classe - Syntaxe
136
Attribut de classe - Déclaration
Déclaration (produitv5.hh)
#pragma once
#include <string>
using reference=unsigned int;
using prix=float;
class produit {
public:
produit(std::string const & nom, prix prixht);
std::string const & nom() const;
virtual float tauxtva() const =0;
prix prixttc() const;
void afficher() const;
private:
reference _ref;
std::string _nom;
prix _prixht;
static reference _compteur;
};
137
Attribut de classe - Définition et initialisation
Attention
La seule déclaration dans le fichier .hh ne suffit pas. Il faut aussi
définir (et initialiser) cet attribut dans le fichier .cc correspondant.
138
Attribut de classe - Définition et initialisation
Définition (produitv5.cc)
#include "produitv5.hh"
#include <iostream>
reference produit::_compteur(0);
139
Attributs de classe
140
Méthodes de classe
Parfois, on veut appeler une méthode sans fournir d’objet sur lequel
appeler la méthode.
141
Méthodes de classe - Exemple
142
Méthodes de classe
Déclaration (produitv6.hh)
class produit {
public:
produit(std::string const & nom, prix prixht);
std::string const & nom() const;
virtual float tauxtva() const =0;
prix prixttc() const;
void afficher() const;
static bool comparaison(produit const & p1, produit const & p2);
Définition (produitv6.cc)
Exemple (Usage)
if (produit::comparaison(prod1,prod2)) ...
144
Méthodes de classe
145
Polymorphisme
146
Polymorphisme
Exemple
Polymorphisme - Exemple
▶ Chaque opération qui porte sur la totalité des produits doit faire
4 boucles.
▶ La définition d’une nouvelle sous-classe de produit demande de
modifier le code de stock.
▶ À éviter 147
Polymorphisme - Exemple
Déclaration (stockv1.hh)
#pragma once
#include "produitv6.hh"
#include <vector>
class stock {
public:
~stock();
void afficher() const;
float tvamoyenne() const;
void afficherperemptions() const;
private:
std::vector<produit *> _prod;
};
149
Polymorphisme - Solution utilisant le polymorphisme
150
Polymorphisme - Solution utilisant le polymorphisme
Définition (stockv1.cc)
#include "stockv1.hh"
#include "produitperissablev6.hh"
#include <iostream>
stock::~stock() {
for (auto p : _prod)
delete p;
}
void stock::afficher() const {
for (auto p : _prod)
p->afficher();
}
float stock::tvamoyenne() const {
float s(0);
for (auto p : _prod)
s += p->tauxtva();
return s/_prod.size();
}
151
Polymorphisme - Solution utilisant le polymorphisme
Problème
Lors de l’exécution d’afficher, les produitperissable n’affichent
pas leur date limite de vente.
Donc…
On déclarera toujours virtual les méthodes pouvant être
redéfinies et destinées à être utilisées par polymorphisme.
152
Polymorphisme
153
Polymorphisme et destructeur
stock::~stock() {
for (auto p : _prod)
delete p;
}
Tous les produits du stock sont détruits, le destructeur est appelé sur
chacun de ces produits…
154
Polymorphisme et destructeur
156
Polymorphisme
dynamic_cast
dynamic_cast
#pragma once
#include "produitcourantv6.hh"
using date=std::string;
class produitperissable: public produitcourant {
public:
produitperissable(std::string const & nom, prix prixht, date const
& peremption);
date const & peremption() const {
return _peremption; }
void afficher() const;
private:
date _peremption;
};
157
dynamic_cast
Problème.
On veut écrire dans stock une méthode d’affichage de toutes les
dates de péremption.
Exemple (qui ne fonctionne pas)
void stock::afficherperemtions() const {
for (auto p : _prod)
std::cout << p->peremption();
}
158
dynamic_cast
159
dynamic_cast
Définition (dynamic_cast)
dynamic_cast est un opérateur de conversion qui permet de
convertir un pointeur (une référence) sur une super-classe vers un
pointeur (une référence) sur une de ses sous-classes.
160
dynamic_cast
afficherperemptions (stockv1.cc)
161
dynamic_cast
Utilisation de dynamic_cast :
162
Polymorphisme
Constructeur virtuel
Problèmes de la classe stock
163
Ajout de produits au stock
void ajouterproduit1(produit p) {
_prod.push_back(&p);
}
164
Ajout de produits au stock
void ajouterproduit2(produitperissable p) {
_prod.push_back(&p);
}
165
Ajout de produits au stock
166
Ajout de produits au stock
void ajouterproduit4(produitperissable * p) {
_prod.push_back(p);
}
167
Ajout de produits au stock
168
Ajout de produits au stock
169
Ajout de produits au stock
170
Constructeur virtuel
class produit {
public:
produit(std::string const & nom, prix prixht);
virtual ~produit() =default;
virtual produit * clone() const =0;
171
Constructeur virtuel
Utilisation (constrvirtuel/stock.hh)
Utile quand la copie des instances d’une classe n’a pas de sens.
Sinon, il faut écrire un constructeur par recopie qui fait effectivement
une copie…Et créer des copies des produits contenus dans le stock à
copier.
173
Constructeur virtuel
incorrect (constrvirtuel/stock.hh)
174
Constructeur virtuel
Ainsi stock est une classe dont les instances peuvent être copiées.
Pour respecter la règle des 3, il faudrait définir aussi l’opérateur
d’affectation.
176
Surcharge d’opérateurs
177
Opérateur d’affectation : Exemple
struct infofichier {
fichierproprietaire _proprietaire;
fichierdate _creation;
};
class fichier {
public:
fichier(fichiernom const & nom, fichiertaille taille);
fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation);
fichier(fichier const & f);
~fichier();
private:
fichiernom _nom;
fichiertaille _taille;
infofichier * _infosuppl;
};
178
Opérateur d’affectation : Exemple
#include "fichierv3.hh"
#include <iostream>
fichier::fichier(fichiernom const & nom, fichiertaille taille)
:_nom(nom),_taille(taille), _infosuppl(nullptr) {
}
fichier::fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation)
:fichier(nom, taille) {
_infosuppl = new infofichier {proprietaire, creation};
}
fichier::fichier(fichier const & f)
:fichier(f._nom, f._taille) {
if (f._infosuppl)
_infosuppl = new infofichier(*f._infosuppl);
}
fichier::~fichier() {
delete _infosuppl;
179
}
Opérateur d’affectation
180
Surcharge d’opérateurs
181
Surcharge d’opérateurs
182
Opérateur d’affectation
Déclaration (fichierv4.hh)
class fichier {
public:
fichier(fichiernom const & nom, fichiertaille taille);
fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation);
fichier(fichier const & f);
~fichier();
fichier & operator=(fichier const & f);
183
Opérateur d’affectation
Définition (fichierv4.cc)
185
Opérateur de sortie
Problème.
std::cout << c est un appel de l’opérateur << sur cout.
Cela est d’ailleurs équivalent à std::cout.operator<<(c).
Il faudrait donc redéfinir cet opérateur dans std::ostream !
Solution.
Les opérateurs >> et << (comme les autres opérateurs) peuvent être
définis comme des fonctions avec la signature suivante :
std::ostream & operator<<(std::ostream & os, C const & c);
std::istream & operator>>(std::istream & os, C & c);
186
Opérateur de sortie - Exemple
Déclaration (fichierv5.hh)
class fichier {
public:
fichier(fichiernom const & nom, fichiertaille taille);
fichier(fichiernom const & nom, fichiertaille taille,
fichierproprietaire proprietaire, fichierdate creation);
fichier(fichier const & f);
~fichier();
fichiernom const & nom() const { return _nom; }
fichiertaille taille() const { return _taille; }
fichier & operator=(fichier const & f);
private:
fichiernom _nom;
fichiertaille _taille;
infofichier * _infosuppl;
};
std::ostream & operator<<(std::ostream & os, fichier const & f);
187
Opérateur de sortie - Exemple
Le code de la fonction doit être donné dans le fichier cc, pas le fichier
hh. Sinon, problème d’édition de liens : fonction définies plusieurs
fois.
Définition (fichierv5.cc)
188
Opérateur de sortie et hiérarchie de classes
(à condition que _prod soit public, ce qui n’est certainement pas une
bonne idée)
Pas d’erreur de compilation, mais ne fait pas ce qui est attendu :
appelle systématiquement l’opérateur de produit (car *p est de type
produit pour le compilateur).
190
Opérateur de sortie et hiérarchie de classes
191
Opérateur de sortie et hiérarchie de classes
192
Opérateur de sortie et hiérarchie de classes
193
Opérateur de sortie et hiérarchie de classes
Erreurs de compilation.
194
Opérateur de sortie et hiérarchie de classes
195
Chapitre 5
Pointeurs intelligents
Allocation de ressources
Pointeur intelligent unique
Pointeur intelligent partagé
Divers
Allocation de ressources
Allocation de ressources
Pointeur intelligent unique
Pointeur intelligent partagé
Divers
Valeurs, Références, Pointeurs
196
Valeurs, Références, Pointeurs
197
Valeurs, Références, Pointeurs
198
Pointeurs
Le partage d’objets est aussi une difficulté : qui doit libérer l’objet
pointé par le pointeur si plusieurs classes/fonctions ont accès au
même objet par pointeur ? quand le faire ?
199
Pointeurs
200
Pointeurs - Information optionnelle
class fichier {
...
private:
fichiernom _nom;
fichiertaille _taille;
infofichier * _infosuppl;
};
201
Pointeurs - Conteneur et polymorphisme
Dans la classe stock, les objets doivent être manipulés par pointeur.
202
Pointeur intelligent unique
Allocation de ressources
Pointeur intelligent unique
Pointeur intelligent partagé
Divers
Pointeurs
stock est robuste car respecte le principe RAII et la règle des 3 mais…
203
unique_ptr
204
unique_ptr
▶ #include <memory>
▶ Type : std::unique_ptr<type>
▶ Constructeur par défaut : Pas d’objet propriété
▶ Allocation dynamique d’un objet propriété par
std::make_unique<type>(parametrescontructeur)
▶ Conversion en bool : true si possède un objet, false sinon
▶ Le constructeur par recopie et l’opérateur d’affectation sont
=delete
▶ std::move permet de transférer la propriété de l’objet pointé
▶ reset permet libérer l’objet propriété
205
unique_ptr - déclaration
std::vector<std::unique_ptr<produit>> _prod;
~stock() =default;
206
unique_ptr - accès
void stock::ajouterproduit(std::unique_ptr<produit> p) {
_prod.push_back(p);
}
void stock::ajouterproduit(std::unique_ptr<produit> p) {
_prod.push_back(std::move(p));
}
stock s;
auto pp = std::make_unique<produitperissable>("pp1", 100, "10/10")
;
/* Erreur , copie impossible .
s . ajouterproduit (pp) ;
*/
s.ajouterproduit(std::move(pp));
210
unique_ptr - constructeur par recopie
211
unique_ptr - constructeur par recopie
212
unique_ptr - constructeur par recopie
Allocation de ressources
Pointeur intelligent unique
Pointeur intelligent partagé
Divers
unique_ptr et propriété partagée
214
Propriété partagée
215
Propriété partagée
217
Propriété partagée
Attention à l’utilisation :
Retour de référence - Incorrect (ressources/partage/stockmain.cc)
218
Propriété partagée
class commande {
public:
commande(std::string const & client)
:_client(client), _produits()
{}
void ajouter(produit const * p) {
_produits.push_back(p);
}
private:
std::string _client;
std::vector<produit const *> _produits;
};
Utilisation (ressources/partage/stockmain.cc)
Ça marche, mais…
220
shared_ptr
221
shared_ptr - Déclaration
Stock (ressources/partage/stockv2.hh)
class stock {
public:
stock() =default;
stock(stock const & s) =delete;
~stock() =default;
stock & operator=(stock const & s) =delete;
void afficher() const;
float tvamoyenne() const;
void afficherperemptions() const;
void ajouterproduit(std::shared_ptr<produit> p);
std::shared_ptr<const produit> rechercher(reference r) const;
private:
std::vector<std::shared_ptr<produit>> _prod;
222
shared_ptr - Utlisation
Stock (ressources/partage/stockv2.cc)
void stock::ajouterproduit(std::shared_ptr<produit> p) {
_prod.push_back(std::move(p));
}
std::shared_ptr<const produit> stock::rechercher(reference r) const{
for (auto const & p : _prod)
if (p->ref() == r)
return p;
return nullptr;
}
Commande (ressources/partage/commandev2.hh)
class commande {
public:
commande(std::string const & client)
:_client(client), _produits()
{}
void ajouter(std::shared_ptr<const produit> p) {
_produits.push_back(std::move(p));
}
private:
std::string _client;
std::vector<std::shared_ptr<const produit>> _produits;
};
main (ressources/partage/stockmainv2.cc)
int main() {
stock s;
auto pp = std::make_shared<produitperissable>("pp1", 100, "10/10")
;
auto ref = pp->ref();
s.ajouterproduit(std::move(pp));
225
shared_ptr - Opérateurs de conversion
std::dynamic_pointer_cast (ressources/partage/stockv2.cc)
226
shared_ptr
227
Pointeurs intelligents - Résumé
Allocation de ressources
Pointeur intelligent unique
Pointeur intelligent partagé
Divers
Déclaration avancée
229
Déclaration avancée
#pragma once
#include <vector>
#include <iostream>
#include <memory>
class produit;
class stock {
230
Déclaration avancée
#include "stockv2.hh"
#include "produit.hh"
#include "produitperissable.hh"
#include <iostream>
void stock::afficher() const {
for (auto const & p : _prod)
p->afficher();
}
231
friend
▶ friend nomclasse;
rend les membres privés de D visibles par toutes les méthodes de
nomclasse.
▶ friend typeretour nomfonction(arguments);
rend les membres privés de D visibles par la fonction
nomfonction.
232
friend
233
Chapitre 6
Exceptions
Définition et syntaxe
Exemple
Détails
Comment gérer les erreurs ?
Définition et syntaxe
Exemple
Détails
Définition
Une exception est un objet qui est créé (« lever une exception ») et
qui :
235
Usage
236
Syntaxe
Syntaxe Exceptions
▶ Levée d’exception
throw variable;
▶ Bloc de gestion d’exceptions
try {
// Instructions à protéger
}
catch (typeexception1 e1) {
// Gestion de l ’ exception de type typeexception1
}
catch (typeexception2 e2) {
// Gestion de l ’ exception de type TypeException2
}
237
Exemple
Définition et syntaxe
Exemple
Détails
Exemple
#include <iostream>
float division(int a, int b) {
if (b == 0) throw 1;
else return static_cast<float>(a) / b;
}
void test() {
int a, b;
std::cin >> a; std::cin >> b;
std::cout << division(a, b) << "\n";
std::cout << "Calcul␣fini\n";
}
238
Exemple
int main() {
try {
test();
std::cout << "Test␣exécuté\n";
}
catch (int i) {
std::cout << "Erreur␣détectée␣" << i << "\n";
}
return 0;
}
239
Exemple
Exemples d’exécution :
▶ Sans exception
2 3
0.666667
Calcul fini
Test exécuté
▶ Avec exception
2 0
Erreur détectée 1
▶ Sans le bloc try du main
2 0
terminate called after throwing an instance of
'int'
Abandon
240
Remarques et conseils
241
Détails
Définition et syntaxe
Exemple
Détails
Ordre des catch
242
Ordre des catch - Exemple
#include <string>
#include <iostream>
class monexception {
public:
monexception(int g)
:_gravite(g) {}
int _gravite;
};
class monexceptionfichier: public monexception {
public:
monexceptionfichier(int g, std::string const & nf)
:monexception(g), _nomfichier(nf) {};
std::string _nomfichier;
};
243
Ordre des catch - Exemple
int main() {
try {
throw monexceptionfichier(3, "f.txt");
}
catch (monexception const & e) {
std::cout << "monexception\n";
}
catch (monexceptionfichier const & e) {
std::cout << "monexceptionfichier\n";
}
return 0;
}
catch(...) permet de gérer tous les types d’exceptions qui n’ont pas
été gérés par les blocs catch précédents.
▶ Il est optionnel.
▶ Il est toujours utilisé comme dernier catch associé à un try.
246
Levée d’exception dans une fonction/méthode
247
Levée d’exception dans une fonction/méthode
248
Levée d’exception dans une fonction/méthode
249
Classes d’exceptions de la bibliothèque standard
http://en.cppreference.com/w/cpp/error/exception
▶ std::exception racine des classes d’exceptions.
Il est conseillé de définir les nouvelles classes exceptions comme
sous-classes de std::exception.
Pour cela, on redéfinira (au moins) la méthode
const char * what()const noexcept.
Cette méthode est appelée dans le cas d’une exception qui sort
du main pour afficher le message d’erreur.
▶ std::bad_alloc levée lors d’une erreur rencontrée par new.
▶ std::bad_cast levée lors d’un dynamic_cast impossible vers
une référence d’une sous-classe.
▶ std::out_of_range levée lors d’un appel incorrect à
std::vector::at, etc. std::logic_error,
std::invalid_argument…(déclarées dans <stdexcept>) 250
Chapitre 7
C++ avancé
Héritage multiple
Espaces de noms
Fonctions anonymes
Classes emboîtées
Modèle de classes
Héritage multiple
Héritage multiple
Espaces de noms
Fonctions anonymes
Classes emboîtées
Modèle de classes
Héritage multiple
Problème
Que se passe-t-il si les classes-mères définissent des attributs de
même nom (éventuellement de types différents) ou des méthodes
de même nom et de même signature ?
252
Héritage multiple - Exemple
class debug {
public:
virtual ~debug() =default;
virtual unsigned int tailleoctets() const =0;
};
253
Héritage multiple - Exemple
class pile {
public:
virtual ~pile() =default;
virtual void empiler(int i) =0;
virtual bool vide() const =0;
virtual void depiler() =0;
virtual int sommet() const =0;
};
254
Héritage multiple - Exemple
255
Héritage multiple - Exemple
256
Héritage multiple - Exemple
class A {
public:
int attribut;
void meth() {}
};
class B {
public:
float attribut;
int meth() {return 0;}
};
Exemple
C c;
c.attribut = 0;
c.meth();
Erreur de compilation :
Attention
La classe C a hérité de deux attributs nommés attribut et de deux
méthodes nommées meth. 259
Héritage multiple
c.A::attribut = 0;
c.B::meth();
260
Espaces de noms
Héritage multiple
Espaces de noms
Fonctions anonymes
Classes emboîtées
Modèle de classes
Espaces de noms
261
Espaces de noms
262
Espaces de noms
263
Fonctions anonymes
Héritage multiple
Espaces de noms
Fonctions anonymes
Classes emboîtées
Modèle de classes
Fonction anonyme
Une fonction anonyme est une fonction… qui n’a pas de nom.
264
Fonction anonyme
265
Fonction anonyme - Exemple
266
Fonction anonyme - Exemple
Soit v un std::vector<int>.
Fonction anonyme - Déclaration complète (lambdas/lambdas.cc)
std::for_each(v.begin(), v.end(),
[](int i)->void{std::cout << i << '␣';});
std::for_each(v.begin(), v.end(),
[](int i){std::cout << i << '␣';});
std::for_each(v.begin(), v.end(),
[](auto i){std::cout << i << '␣';});
267
Fonction anonyme - Exemple
268
Fonction anonyme - Exemple
269
Fonction anonyme - Capture
270
Fonction anonyme - Capture
271
Fonction anonyme - Capture
272
Classes emboîtées
Héritage multiple
Espaces de noms
Fonctions anonymes
Classes emboîtées
Modèle de classes
Classes emboîtées
273
Classes emboîtées
class graphe {
public:
class sommet {
public:
using identifiant = unsigned int;
private:
struct arcsortant {
identifiant _extremite;
std::string _etiquette;
};
public:
sommet(std::string const & et)
:_etiquette(et), _id(++_compteur) {}
identifiant id() const { return _id; }
std::vector<identifiant> voisins() const;
std::string const & etiquette() const { return _etiquette; }
void ajouterarc(std::string const & et,identifiant extremite);
274
Classes emboîtées
private:
std::string _etiquette;
identifiant _id;
std::list<arcsortant> _arcs;
static identifiant _compteur;
};
public:
sommet::identifiant ajoutersommet(std::string const & et);
sommet const & accessommet(sommet::identifiant id) const;
void ajouterarc(sommet::identifiant origine, sommet::identifiant
extremite, std::string const & etiquette);
private:
std::list<sommet>::iterator chercher(sommet::identifiant id);
std::list<sommet>::const_iterator chercher(sommet::identifiant
id) const;
private:
std::list<sommet> _sommets;
}; 275
Classes emboîtées
graphe::sommet::identifiant graphe::sommet::_compteur(0);
std::vector<graphe::sommet::identifiant> graphe::sommet::voisins()
const {
std::vector<identifiant> result;
for (auto const & av : _arcs)
result.push_back(av._extremite);
return result; 276
}
Classes emboîtées
std::list<graphe::sommet>::iterator graphe::chercher(sommet::
identifiant id) {
auto f = std::find_if(_sommets.begin(), _sommets.end(), [id](auto
const & a){return a.id() == id;});
if (f == _sommets.end())
throw std::invalid_argument(std::to_string(id));
else
return f;
}
std::list<graphe::sommet>::const_iterator graphe::chercher(sommet::
identifiant id) const {
auto f = std::find_if(_sommets.begin(), _sommets.end(), [id](auto
const & a){return a.id() == id;});
if (f == _sommets.end())
throw std::invalid_argument(std::to_string(id));
else
return f; 278
Classes emboîtées
int main() {
graphe g;
graphe::sommet::identifiant paris(g.ajoutersommet("Paris"));
auto angers(g.ajoutersommet("Angers"));
auto nantes(g.ajoutersommet("Nantes"));
g.ajouterarc(angers, nantes, "Bus");
g.ajouterarc(nantes, angers, "Bus");
g.ajouterarc(angers, paris, "Train");
g.ajouterarc(paris, nantes, "Avion");
std::cout << "Voisins␣de␣" << g.accessommet(angers).etiquette() <<
"\n";
auto voisinsangers(g.accessommet(angers).voisins());
for (auto v : voisinsangers)
std::cout << g.accessommet(v).etiquette() << "␣";
return 0;
}
279
Modèle de classes
Héritage multiple
Espaces de noms
Fonctions anonymes
Classes emboîtées
Modèle de classes
Modèle de classes
Attention
Un modèle de classes n’est pas une classe.
#pragma once
#include <vector>
281
Modèle de classes
282
Modèle de classes
#include "pile.hh"
#include <string>
int main() {
pile<int> pint; pile<std::string> pstring;
pint.empiler(2);
pstring.empiler("essai");
return 0;
}
283
Modèle de classes
284
Chapitre 8
Initiation au développement d’interfaces
graphiques avec Qt
Arborescence de composants
Signaux et slots
Placement des composants
Interfaces graphiques et C++
Arborescence de composants
Signaux et slots
Placement des composants
Principe de Qt
286
QObject : Arborescence d’objets
287
QWidget : Arborescence de composants
288
QApplication : Application
289
Exemple d’application Qt minimale
#include <QtWidgets>
290
Exemple d’application Qt minimale
1 cmake_minimum_required(VERSION 3.1.0)
2 project(qtpremier CXX)
3
4 set(CMAKE_CXX_STANDARD 14)
5 set(CMAKE_CXX_STANDARD_REQUIRED on)
6 set(CMAKE_CXX_EXTENSIONS off)
7
8 find_package(Qt5Widgets REQUIRED)
9 set(CMAKE_INCLUDE_CURRENT_DIR ON)
10 set(CMAKE_AUTOMOC ON)
11 set(CMAKE_AUTOUIC ON)
12
17 add_executable(qtpremier main.cc)
18 target_link_libraries(qtpremier Qt5::Widgets) 291
Construction d’une application Qt
292
Quelques composants
Et bien d’autres
http://doc.qt.io/qt-5/widget-classes.html
293
Arborescence de composants
#include <QtWidgets>
295
Arborescence de composants
296
Signaux et slots
Arborescence de composants
Signaux et slots
Placement des composants
Signaux et slots - Principe
297
Signaux - Principe
298
Slots - Principe
299
Signaux - Fonctionnement
300
Signaux - Le Meta-Object Compiler
Le moc gère des extensions au langage C++ propres à Qt, dont les
signaux et les slots.
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
301
Signaux - Exemple
#pragma once
#include <QObject>
#include <vector>
#include <string>
class setofstring: public QObject {
Q_OBJECT
public:
void ajoutervaleur(std::string const &);
void supprimervaleur(std::string const &);
bool contientvaleur(std::string const &) const;
signals:
void valeurajoutee(std::string);
void valeursupprimee(std::string);
void taillemodifiee(std::size_t);
private:
std::vector<std::string> _contenu;
};
302
Signaux - Exemple
#include "setofstring.hh"
#include <algorithm>
303
Signaux - Exemple
304
Slots
305
Slots
306
Slots
#include <QtWidgets>
307
Slots
#include "fenetrev1.hh"
fenetre::fenetre()
:QWidget() {
resize(320,200);
_saisie = new QLineEdit("", this);
_ajout = new QPushButton("Ajouter", this);
_taille = new QLabel("taille", this);
_quitter = new QPushButton("Quitter", this);
_saisie->setGeometry(10,10,300,40);
_ajout->setGeometry(10,50,300,50);
_taille->move(10,100);
_quitter->setGeometry(10,140,300,50);
}
308
Connexion slot/signal
Connexion
La méthode (de classe) QObject::connect permet de connecter un
signal d’un objet à un slot d’un objet.
Paramètres : Pointeur sur l’objet émetteur, Signal émis, Pointeur sur
l’objet receveur, Slot déclenché.
309
Connexion slot/signal
310
Déclaration de slots
#include <QtWidgets>
#include "setofstring.hh"
class fenetre: public QWidget {
Q_OBJECT
public:
fenetre(setofstring& sos);
public slots:
void onclicajout();
void ontaillemodifiee(std::size_t);
void onajoutervaleur(std::string const &);
private:
setofstring& _sos;
QLineEdit * _saisie;
QPushButton * _ajout;
311
Connexions
Connexions (qt/slotssignals/fenetrev2.cc)
fenetre::fenetre(setofstring& sos)
:QWidget(), _sos(sos) {
resize(320,200);
_saisie = new QLineEdit("", this);
_ajout = new QPushButton("Ajouter", this);
_taille = new QLabel("taille", this);
_quitter = new QPushButton("Quitter", this);
_saisie->setGeometry(10,10,300,40);
_ajout->setGeometry(10,50,300,50);
_taille->move(10,100);
_quitter->setGeometry(10,140,300,50);
connect(_ajout, &QPushButton::clicked, this, &fenetre::onclicajout
);
connect(&_sos, &setofstring::taillemodifiee, this, &fenetre::
ontaillemodifiee);
connect(&_sos, &setofstring::valeurajoutee, this, &fenetre::
onajoutervaleur); 312
}
Définition de slots
void fenetre::onclicajout() {
_sos.ajoutervaleur(_saisie->text().toStdString());
}
Arborescence de composants
Signaux et slots
Placement des composants
Placement des composants
▶ Fastidieux.
▶ Ne gère pas l’agrandissement des fenêtres.
314
Principe d’un gestionnaire de placement
315
Principaux gestionnaires de placement
http://doc.qt.io/qt-5/layout.html
▶ QHBoxLayout
▶ QVBoxLayout
▶ QGridLayout
▶ … 316
QHBoxLayout
QHBoxLayout (qt/layout/main.cc)
318
QVBoxLayout
QVBoxLayout (qt/layout/main.cc)
319
QGridLayout
QGridLayout (qt/layout/main.cc)
320
QGridLayout
▶ Le composant à ajouter
▶ Les coordonnées de la case (ligne, colonne, en commençant à 0)
▶ Le nombre de cases (verticalement, horizontalement) (par défaut
1,1)
321
QGridLayout - Étirement
322
QGridLayout
QGridLayout (qt/layout/main.cc)
323
QGridLayout - Étirement
324
QGridLayout - Séparateurs
325