2. Cours semaine 1 jour 5 Cours C++ 2
Plan du jour
Surcharge de fonctions
Constantes et pointeurs en C et C++
Utilisation du préprocesseur
3. Cours semaine 1 jour 5 Cours C++ 3
Surcharge de fonctions
Parfois, dans l’univers quotidien, un même
mot peut signifier plusieurs choses
différentes
Polysémie (pour les noms dans ce cas)
De même, un verbe peut avoir plusieurs
sens et représenter des actions différentes
Polysémie (pour les verbes cette fois)
Polymorphisme chez les objets
4. Cours semaine 1 jour 5 Cours C++ 4
Surcharge de fonctions (2)
Dans un système orienté objet, le
polymorphisme indique qu’un même appel
de fonction se traduira par l’exécution
possible de traitements différents, en
fonction du contexte
Cependant, dans le cas du polymorphisme,
il s’agit de fonctions ayant toujours la même
signature
seDéplacer( float mètres );
5. Cours semaine 1 jour 5 Cours C++ 5
Surcharge de fonctions (3)
Il est possible, en C++, de définir plusieurs
fois une même fonction
C’est à dire d’utiliser plusieurs fois le même
nom de fonction
Cependant, cette possibilité est
indissociable de la condition que chacune
des fonctions doit avoir des arguments
différents
6. Cours semaine 1 jour 5 Cours C++ 6
Surcharge de fonctions (4)
En reprenant l’exemple du déplacement, un
classe pourra offrir autant de fonctions de
déplacement que de types possibles
seDéplacer( float mètres ),
seDéplacer( double mètres ),
seDéplacer( int mètres ),
seDéplacer( long mètres ),
seDéplacer( Longueur mètres ),
Etc.
7. Cours semaine 1 jour 5 Cours C++ 7
Surcharge de fonctions (5)
Cette possibilité de définir plusieurs
fonctions avec des arguments de type
différent s’appelle la surcharge de fonction
Et ce n’est pas le polymorphisme…
Cette surchage ne se limite pas aux
opérations à un argument mais il y a des
limites à tous. Ceci ne marche pas :
seDéplacer( int mètres, int angle );
seDéplacer( int angle, int mètres );
8. Cours semaine 1 jour 5 Cours C++ 8
Surcharge de fonctions (6)
Attention ! C et C++ sont des langages
typés (il y a des types) mais le typage est
faible (il y a quand même des void, y
compris sur les pointeurs…)
S’il est possible d’avoir une surcharge sur
les fonctions par les arguments, il est
cependant impossible d’avoir une surcharge
sur les types de retour
9. Cours semaine 1 jour 5 Cours C++ 9
Surcharge de fonctions (7)
La surcharge sur les types de retour n’est
pas autorisée car il n’est après tout pas
obligatoire de tenir compte de la valeur de
retour d’une fonction…
Le type de la partie gauche d’une affectation
serait un bon mécanisme de sélection mais cette
partie gauche n’existe pas obligatoirement :
void f(); n’aura jamais de partie gauche !
int f();
10. Cours semaine 1 jour 5 Cours C++ 10
Surcharge de fonctions (8)
#include <iostream>
using namespace std;
int f( int i )
{
return i * 2;
}
void main()
{
int i;
i = f( 3 );
cout << i << endl;
f( 4 ); // même pas de warning !
cout << i << endl;
i = f( 5 );
cout << i << endl;
}
11. Cours semaine 1 jour 5 Cours C++ 11
Surcharge de fonctions (9)
Nous avons déjà vu l’overloading de
fonction !
Rappeler vous, hier, Vektor possédait deux
constructeurs qui renvoyaient tous les deux des
Vektor mais avait des arguments différents
L’overloading de fonctions du C est une
bonne chose de C/C++ mais complique
parfois l’apprentissage des librairies !
12. Cours semaine 1 jour 5 Cours C++ 12
Constantes
En C, les constantes se définissent avec la
directive de précompilation #define
#define PI 3.14159265358979323846
Lors de la phase de précompilation, le
préprocesseur remplace la première chaîne
« PI » par « 3.14159… » à chaque fois que
« PI » est rencontré dans un fichier
Le remplacement se fait mot pour mot, de
manière bête et méchante
13. Cours semaine 1 jour 5 Cours C++ 13
Constantes (2)
Le problème majeur du #define correspond
à ce remplacement direct et volontairement
« à l’aveugle »
Un #define ne comporte AUCUN
renseignement de typage
La possibilité de définir des constantes non
typées dans un langage typé est finalement
assez problématique
14. Cours semaine 1 jour 5 Cours C++ 14
Constantes (3)
C++ résoud le problème en introduisant le terme
const qui signifie qu’une définition de variable
correspond à une valeur fixe nommée (comme
dans le #define) et typée
const float pi = 3.14159265358979323846;
Attention, les constantes sont désormais définies
comme des variables mais ne sont pas pour autant
globales même si elles sont dans des fichiers
d’include
On peut les inclure plusieurs fois !
15. Cours semaine 1 jour 5 Cours C++ 15
Constantes (4)
Il est aussi possible de définir des pointeurs
constants
Exemple : un pointeur invariable sur un int (lire
les définitions de la droite vers la gauche)
const int* ip;
Un pointeur sur un const int : le pointeur peut changer, mais
pas l’entier pointé !
int const* ip2;
Un pointeur vers un entier invariable
int* const ip3;
Un pointeur invariable vers un int, enfin…
16. Cours semaine 1 jour 5 Cours C++ 16
Constantes (5)
Il est possible de passer des arguments
invariables à une fonction
Cela permet d’être sûr qu’ils ne seront pas
modifiés (c’est vérifié pendant la compilation)
void f( const int* i )
Le paramètre i doit être considéré comme une
constante par f
17. Cours semaine 1 jour 5 Cours C++ 17
Constantes (6)
Le spécifieur const est applicable aux types
de base et aux pointeurs.
Il est également applicable dans les classes
et « aux classes »
Certaines variables d’une classe peuvent être
des constantes
Certains objets (instances d’une classe) peuvent
être constants
18. Cours semaine 1 jour 5 Cours C++ 18
Constantes (7)
Les constantes dans les classes
Certaines données d’une classe peuvent être
considérées comme constantes. Il faut
cependant parfois les initialiser !
Ceci se fait de manière assez bizarre : il faut
appeler le constructeur du type constant, même
s’il s’agit d’un type de base !
19. Cours semaine 1 jour 5 Cours C++ 19
Constantes (8)
class Test
{
const int size;
public:
Test( int sz );
void getSize();
};
Test::Test( int sz ) : size( sz )
{}
void Test::getSize()
{
cout << size << endl;
}
#include <iostream>
using namespace std;
void main()
{
Test tt( 4 );
tt.getSize();
}
20. Cours semaine 1 jour 5 Cours C++ 20
Constantes (9)
Les objets peuvent aussi être définis comme
constants
Cependant, pour garantir la constance des
objets, il faut garantir que certaines
méthodes ne modifient pas les variables
internes et qu’elles sont donc applicables
aux objets constants
21. Cours semaine 1 jour 5 Cours C++ 21
Constantes (10)
class Test2 {
int size;
public:
Test2( int sz );
int getSize() const;
void setSize( int s );
};
Test2::Test2( int sz ) : size( sz )
{}
int Test2::getSize() const {
return size;
}
void Test2::setSize( int s ) {
size = s;
}
22. Cours semaine 1 jour 5 Cours C++ 22
Constantes (11)
#include <iostream>
using namespace std;
void main()
{
Test2 t1( 2 );
const Test2 t2( 3 );
cout << t1.getSize() << endl;
cout << t2.getSize() << endl;
t1.setSize( 4 );
t2.setSize( 6 ); // ne compile pas !
cout << t1.getSize() << endl;
cout << t2.getSize() << endl;
}
23. Cours semaine 1 jour 5 Cours C++ 23
Constantes (12)
Mais voilà, parfois, on veut pouvoir changer
même ce qui est constant…
En C/C++ : flexibilité et pragmatisme…
Il est possible de dire que des variables
internes d’un objet constant peuvent
changer à l’aide du mot clé mutable
24. Cours semaine 1 jour 5 Cours C++ 24
Constantes (13)
class Test2
{
int size;
mutable int i;
public:
Test2( int sz );
int getSize() const;
int getNum() const;
void setSize( int s );
};
Test2::Test2( int sz ) : size( sz ), i( 0 )
{}
int Test2::getSize() const
{
i++; // const !
// mais mutable…
return size;
}
int Test2::getNum() const
{
return i;
}
void Test2::setSize( int s )
{
size = s;
}
25. Cours semaine 1 jour 5 Cours C++ 25
Constantes (14)
#include <iostream>
using namespace std;
void main()
{
Test2 t1( 2 );
const Test2 t2( 3 );
cout << t1.getSize() << " " << t1.getNum() << endl;
cout << t2.getSize() << " " << t2.getNum() << endl;
t1.setSize( 4 );
//t2.setSize( 6 ); // toujours pas possible (const pas mutable)
cout << t1.getSize() << " " << t1.getNum() << endl;
cout << t2.getSize() << " " << t2.getNum() << endl;
}
26. Cours semaine 1 jour 5 Cours C++ 26
Constantes (15)
Les #define permettaient de définir des
valeurs constantes mais également des
fonctions « constantes »
C++ offre const pour les valeurs
C++ offre inline pour les fonctions
Version appauvrie mais bien plus sûre du
#define
27. Cours semaine 1 jour 5 Cours C++ 27
Constantes (16)
Les fonctions inline ne doivent cependant
pas être utilisées de manière abusive :
Elles ont tendance à réduire les performances,
Elles permettent de contourner un mauvais
design
Leur réplication par copier/coller peut apporter
de nombreux problèmes (comme d’habitude
avec le copier/coller…)
Pas utilisable ailleurs que dans le fichier
28. Cours semaine 1 jour 5 Cours C++ 28
Constantes (17)
#include <iostream>
using std::cout;
using std::endl;
inline void f( int i )
{
cout << "inline f sur " << i << endl;
}
void main()
{
f( 1 );
}
29. Cours semaine 1 jour 5 Cours C++ 29
Constantes (18)
Les fonctions peuvent également avoir des
valeurs par défaut dans leur signature
Pour les types élémentaires sauf les pointeurs et
pas pour les classes !
Les valeurs par défaut se mettent dans les
signatures des headers ou dans les
définitions de fonction
float p( float r = 2.0f )
Si aucune valeur n’est fournie, alors 2.0f
30. Cours semaine 1 jour 5 Cours C++ 30
Pointeurs
Les pointeurs sont assez compliqués à
suivre et méritent la plus grande attention
Ils sont à l’origine de la majorité des problèmes
en C et C++
Remarque : "int* i" est équivalent à "int *i"
31. Cours semaine 1 jour 5 Cours C++ 31
Pointeurs (2)
Nous avons vu que les pointeurs permettent
de passer la référence à une variable (une
case mémoire) plutôt qu’une copie de cette
variable
Les pointeurs permettent le passage par
référence, plus efficace que le passage par
valeur
Les pointeurs permettent également de
prédéfinir des variables dont il n’est pas
possible de connaître la taille a priori
32. Cours semaine 1 jour 5 Cours C++ 32
Pointeurs (3)
Lorsqu’une fonction passe l’adresse d’une
variable à une autre fonction, elle prend des
risques
La fonction appelée peut faire n’importe quoi
Elle peut donc détruire toute la zone appelée de
manière irrémédiable alors qu’elle devait
officiellement n’en modifier qu’une petite
partie
Le passage par valeur peut alors s’avérer
utile…
33. Cours semaine 1 jour 5 Cours C++ 33
Pointeurs (4)
Cependant, le passage par valeur doit
normalement avoir lieu avec des types de
base
Si une fonction stockait uniquement un
pointeur dans sa définition avant de réserver
un bloc de mémoire, il faudrait fournir la
copie du bloc mémoire en plus des types de
base
Cela a l’air bien compliqué !
34. Cours semaine 1 jour 5 Cours C++ 34
Pointeurs et copie
C++, en plus des fonctions de base de
construction et de destruction de classe,
fournit un moyen de créer des copies de
l’objet courant en utilisant une fonction
« par défaut » supplémentaire :
Le copy constructor, constructeur de copie
Le nom de la fonction reprend le nom de la
classe, de même que le nom de l’argument
35. Cours semaine 1 jour 5 Cours C++ 35
Pointeurs et copie (2)
Si vous travaillez avec une classe X et avez
besoin d’un copy constructor, vous pouvez
le définir avec les constructeurs et
destructeurs
X::X() // constructeur
X::~X() // destructeur
X::X(const X& x ) // constructeur de copie
L’opérateur est connu sous le sobriquet de
X(X&)
36. Cours semaine 1 jour 5 Cours C++ 36
Pointeurs et copie (3)
#include <fstream>
#include <string>
using namespace std;
ofstream out( "MaClasse.txt" );
class MaClasse
{
public:
string name;
static int objectCount;
MaClasse( const string& id = "" ) : name( id )
{
++objectCount;
print( "MaClasse()" );
}
37. Cours semaine 1 jour 5 Cours C++ 37
Pointeurs et copie (4)
~MaClasse()
{
--objectCount;
print( "~MaClasse()" );
}
// copy constructor
MaClasse( const MaClasse& h ) : name( h.name )
{
name += " copy";
++objectCount;
print( "MaClasse( const MaClasse& ) - appel du copy const" );
}
38. Cours semaine 1 jour 5 Cours C++ 38
Pointeurs et copie (5)
void print(const string& msg = "") const
{
if( msg.size() != 0 )
out << msg
<< endl;
out << 't' << name << ": "
<< "objectCount = " <<objectCount
<< endl << endl;
}
};
39. Cours semaine 1 jour 5 Cours C++ 39
Pointeurs et copie (6)
int MaClasse::objectCount = 0;
// appel et retour PAR VALEUR :
MaClasse f( MaClasse maClasse )
{
maClasse.print( "argument maClasse dans f()" );
out << endl << "! sortie de f()" << endl << endl;
return maClasse;
}
40. Cours semaine 1 jour 5 Cours C++ 40
Pointeurs et copie (7)
void main()
{
MaClasse h( "h" );
out << endl << "! h2 appel de f()" << endl << endl;
MaClasse h2 = f( h );
h2.print( "! h2 apres appel de f()" );
out << endl << "! appel de f(),«
<< " pas de valeur de retour" << endl << endl;
f( h );
out << endl << "! apres appel de f(),«
<< " pas de valeur de retour" << endl << endl;
}
41. Cours semaine 1 jour 5 Cours C++ 41
Directives
Les directives utilisables avec un
préprocesseur C/C++ sont standardisées
dans leurs bases
Chaque compilateur pourra cependant en
offrir d’autres supplémentaires
C’est bien sûr le cas de la plate-forme
Microsoft
42. Cours semaine 1 jour 5 Cours C++ 42
Directives (2)
Il existe trois conditionnelles de
précompilation :
#if expression
Permet de tester une constante ou une expression
#ifdef expression
Permet de tester l’existence d’un identifiant
#idndef expression
Permet de tester l’inexistence d’un identifiant
43. Cours semaine 1 jour 5 Cours C++ 43
Directives (3)
La directive #ifdef ou #ifndef sont des
contractions de #if « test »
#if defined( __EXPRESSION__ )
est équivalent
à #ifdef __EXPRESSION__
#if !defined(__EXPRESSION__ )
est équivalent à
#ifndef __EXPRESSION__
44. Cours semaine 1 jour 5 Cours C++ 44
Directives (4)
Comme nous sommes en train de réaliser
des tests, d’autres possibilités existent
#else
#elif (else if)
#endif
45. Cours semaine 1 jour 5 Cours C++ 45
Directives (5)
À partir de ces conditionnelles, il est par
exemple possible d’inclure dans le
programme certaines parties de code plutôt
que d’autres :
#ifdef __MY_MAIN__
int entierGlobal;
#else
extern int entierGlobal;
#endif
46. Cours semaine 1 jour 5 Cours C++ 46
Directives (6)
Ce genre de technique permet de ne
conserver qu’un seul fichier d’inclusion (les
.h) contenant la totalité des variables
globales, s’il y en a, et de distinguer les
traitements en fonctions du fichier source
Le fichier source contenant la fonction main()
comportera par exemple, avant l’inclusion des
.h, une directive
#define __MY_MAIN__
47. Cours semaine 1 jour 5 Cours C++ 47
Directives (7)
Les directives permettent d’influencer
l’écriture finale des fichiers source, elles
permettent également d’influencer les
traitements internes
#line 24
Dit qu’à partir de la ligne où se trouve le #line, nous
sommes à la ligne 24
#line 24 "NouveauNom.c"
Change la ligne courante et le nom apparent du
fichier, utilisé dans les messages d’erreurs
48. Cours semaine 1 jour 5 Cours C++ 48
Directives (8)
Il est également possible de créer des
erreurs de compilation
#if !defined( __EXPRESSION__ )
#error Vous n’avez pas définit EXPRESSION
#endif
49. Cours semaine 1 jour 5 Cours C++ 49
Directives (9)
Enfin, il existe des directives permettant de
raffiner la compilation en fonction de la
machine sur laquelle on se trouve
Prise en compte d’un système d’exploitation
particulier (il existe plusieurs versions de
Windows !)
Prise en compte d’un jeu d’instructions
particulier : tous les processeurs n’offrent pas
les même possibilités (il existe plusieurs
processeurs chez Intel !)
50. Cours semaine 1 jour 5 Cours C++ 50
Directives (10)
Ces directives particulières sont identifiées
par #pragma
C, et C++, sont après tout des langages
pragmatiques…
Les #pragma permettent de s’enfoncer au
cœur du processus de transformation du
code C/C++ en assembleur qui sera ensuite,
pendant la phase d’édition de lien,
transformer en langage machine
51. Cours semaine 1 jour 5 Cours C++ 51
Directives (11)
#pragma alloc_text permet de modifier le
nom assembleur des fonctions
Typiquement, f( int x ) se traduit normalement
en _f_int. Cela peut se changer…
#pragma auto_inline permet de modifier le
traitement des fonctions inline
Cela revient presque à dire que l’on ne veut pas
appliquer tel que l’équivalent inline de #define
MAX( A, B )
52. Cours semaine 1 jour 5 Cours C++ 52
Directives (12)
#pragma bss_seg permet de regrouper
toutes les variables non initialisées dans un
même bloc. Cela accélère le temps de
chargement
#pragma check_stack permet de modifier
le processus de vérification de la pile, pour
les parties de code n’utilisant pas le tas
53. Cours semaine 1 jour 5 Cours C++ 53
Directives (13)
#pragma code_seg permet de spécifier
l’endroit où l’on veut stocker les morceaux
de code des fonctions
#pragma const_seg permet de spécifier
l’endroit où l’on veut stocker les constantes
du programme
Crée un segment read-only
54. Cours semaine 1 jour 5 Cours C++ 54
Directives (14)
#pragma comment permet de placer un
commentaire dans le fichier assembleur (!).
À utiliser dans le cas où l’on vous retire la
licence Visual Dev…
#pragma component permet d’influer sur
la collecte des informations dans le code
source C++
#pragma component( minrebuild, off ) permet
de supprimer le minimum rebuild dans Visual
C++ (et les informations associées)
55. Cours semaine 1 jour 5 Cours C++ 55
Directives (15)
#pragma data_seg permet de regrouper
toutes les variables dans un même bloc.
Cela accélère le temps de chargement
#pragma function permet d’influer sur le
traitement du code. Il force la création
d’appels de fonction plutôt que l’utilisation
d’inline. Cela influe sur l’espace disque du
programme compilé
56. Cours semaine 1 jour 5 Cours C++ 56
Directives (16)
#pragma hdrstop influe sur le traitement
des fichiers d’inclusion précompilés
#pragma include_alias permet de résoudre
les problèmes 8.3 de la FAT
#pragma init_seg dit comment le code de
chargement du programme en mémoire est
exécuté
57. Cours semaine 1 jour 5 Cours C++ 57
Directives (17)
#pragma inline_depth permet de contrôler
le ratio accepté d’inline ou d’appel de
fonction. Influe sur la taille du programme
et le temps d’exécution (compromis
espace/temps).
#pragma inline_recursion influe sur la
réécriture en inline ou en fonction des
fonctions récursives (compromis
espace/temps)
58. Cours semaine 1 jour 5 Cours C++ 58
Directives (18)
#pragma intrinsic dit au compilateur de
générer les opérations intrinsèques en inline
ou en fonction (espace/temps). Par exemple,
s’applique à la fonction de calcul de la
valeur absolue abs( int ).
#pragma message affiche un message à
l’écran pendant l’exécution de la
compilation
59. Cours semaine 1 jour 5 Cours C++ 59
Directives (19)
#pragma once indique au compilateur de
n’ouvrir un fichier qu’une fois pendant la
compilation
Regardez le contenu d’un fichier StdAfx.h…
#pragma optimize pour indiquer un mode
d’optimisation du code. Plusieurs options.
#pragma pack permet d’aligner des
structures de données sur un nombre
d’octets spécifiques
60. Cours semaine 1 jour 5 Cours C++ 60
Directives (20)
#pragma pointers_to_members permet de
modifier le traitement des pointeurs vers les
data members d’une classe
#pragma setlocale permet de spécifier le
jeu de caractères « ASCII » à utiliser pour la
compilation. Permet d’avoir un support pour
les jeux de caractères nationaux.
61. Cours semaine 1 jour 5 Cours C++ 61
Directives (21)
#pragma vtordisp influe sur l’appel de fonctions
d’une classe fille vers la classe mère, dans le cadre
de l’héritage (une fille peut redéfinir une fonction
de la classe mère et tout de même appeler la
fonction de la classe mère pour réaliser certains
traitements)
#pragma warning modifie le comportement du
compilateur pour le traitement des warning
(erreurs non fatales à la compilation)