| auteur : Marshall Cline |
Quand le compilateur évalue l'appel d'une fonction inline, le code complet de cette fonction est inséré dans le code de l'appelant (c'est
le même principe que ce qui se passe avec un #define). Cela peut, parmi beaucoup d'autres choses, améliorer les performances, étant donné
que l'optimiseur peut intégrer directement le code appelé, voire optimiser le code appelé en fonction du code appelant.
Il y a plusieurs façons d'indiquer qu'une fonction est inline, certaines impliquant le mot-clé inline, d'autres non. Peu importe
comment une fonction est déclarée inline, c'est une demande que le compilateur est autorisé à ignorer : il peut en évaluer certaines, toutes
ou même aucune. (Ne soyez pas découragés si cela semble désespérément vague. La flexibilité de ce qui précède est un avantage important :
le compilateur peut gérer de grosses fonctions différemment des petites, de plus, cela permet au compilateur de générer du code facile à
déboguer si vous spécifiez les bonnes options de compilation.)
|
| auteur : Marshall Cline |
Supposons l'appel suivant à une fonction g() :
void f ()
{
int x = ;
int y = ;
int z = ;
...code that uses x, y and z...
g (x, y, z);
...more code that uses x, y and z...
}
|
En supposant que l'on ait une implémentation typique de C++, possédant des registres et une pile, les registres et les paramètres sont
déposés sur la pile juste avant l'appel de g(). Les paramètres sont ensuite retirés de la pile lors de l'entrée dans g(), redéposés lors
de la sortie de g() et finalement relus dans f(). Cela fait un certain nombre de lectures et écritures inutiles, spécialement dans le cas
ou le compilateur a la possibilité d'utiliser les registres pour les variables x, y et z : chaque variable pourrait être écrite deux fois
(en tant que registre et en tant que paramètre) et lue deux fois aussi (lors de son utilisation dans g() et pour restaurer les registres
au retour dans f()).
void g (int x, int y, int z)
{
...code that uses x, y and z...
}
|
Si le compilateur évalue l'appel de g() en tant qu'inline, toutes ces lectures et écritures disparaissent. Les registres n'auront pas
besoin d'être écrits deux fois et les paramètres n'auront pas besoin d'être empilés ni désempilés, étant donné que l'optimiseur
saura qu'ils sont déjà dans les registres.
Bien entendu, votre configuration particulière sera différente, et il existe de nombreux cas qui sont en dehors du cas de figure présenté
ici, mais l'exemple ci-dessus sert à illustrer ce qui peut se passer lors de l'intégration inline.
|
| auteur : Marshall Cline |
En C pur, vous pouvez réaliser des "structs (structures) encapsulées" en mettant un void * dans un struct, où le void * pointe sur
les vraies données qui sont inconnues aux utilisateurs du struct. Ainsi ces même utilisateurs du struct ne peuvent pas interpréter
les données pointées par void *, mais les fonctions d'accès peuvent elles convertir du void * vers le type de données caché. Ce qui
donne une forme de l'encapsulation.
Malheureusement ceci impose de renoncer à la sûreté de type, et impose également un appel de fonction pour accéder même aux zones
insignifiantes de la structure (si vous permettiez l'accès direct au champs de la structure, chacun pourrait accéder directement à toute
la structure puisqu'il connaîtrait nécessairement les données pointées par void *, et il deviendrait difficile de changer la structure de
données sous jacente).
Le temps d'appel de fonction est court, mais il s'ajoute à chaque appel. Les classes C++ permettent à ces appels de fonction d'être
insérés inline. Ceci vous laisse la sûreté de l'encapsulation avec en plus la vitesse des accès directs. En outre, les types des
paramètres de ces fonctions inline sont contrôlés par le compilateur, ce qui est une amélioration par rapport aux macros #define du C.
|
| auteur : Marshall Cline |
Parce que les #define sont mauvais. Mais parfois il faut les utiliser.
Contrairement aux #define, les fonctions inline évitent des erreurs très difficiles à tracer, étant donné que les fonctions inline évaluent
toujours chaque argument une et une seule fois. En d'autres mots, l'invocation d'une fonction inline est sémantiquement identique à
l'invocation d'une fonction classique, avec la seule différence de la rapidité.
# define unsafe ( i ) \
( (i) >= 0 ? (i) : - (i) )
inline
int safe (int i)
{
return i >= 0 ? i : - i;
}
int f ();
void userCode (int x)
{
int ans;
ans = unsafe (x+ + );
ans = unsafe (f ());
ans = safe (x+ + );
ans = safe (f ());
}
|
Une autre différence est que les types des paramètres sont vérifiés, et les conversions nécessaires effectuées.
Les macros sont à proscrire, ne les utilisez que si vous n'avez pas d'autre alternative.
|
| auteur : Marshall Cline |
Quand vous déclarez une fonction inline, elle a exactement l'aspect d'une fonction normale :
Mais quand vous définissez une fonction inline, vous ajoutez au début de la définition de la fonction le mot-clé inline, et vous
mettez la définition dans le fichier d'en-tête :
inline void f (int i, char c)
{
}
|
|
| auteur : Marshall Cline |
Quand vous déclarez inline une fonction membre, elle a exactement l'aspect d'une fonction membre normale :
class Fred {
public :
void f (int i, char c);
} ;
|
Mais quand vous définissez une fonction membre inline, vous ajoutez au début de la définition de la fonction membre le mot-clé inline,
et vous mettez la définition dans un fichier d'en-tête :
inline void Fred:: f (int i, char c)
{
}
|
Il est habituellement impératif que la définition de la fonction (la partie entre {... }) soit placée dans un fichier d'en-tête.
Si vous mettiez inline la définition d'une fonction dans un fichier d'implémentation cpp, et si cette fonction était appelée d'un autre
fichier cpp, vous obtiendriez "une erreur externe" (fonction non définie) au moment de l'édition de liens.
|
| auteur : Marshall Cline |
Oui : définissez la fonction de membre dans le corps de classe elle-même :
class Fred
{
public :
void f (int i, char c)
{
}
} ;
|
Bien que ce soit plus facile pour la personne qui écrit la classe, c'est aussi plus dur pour le lecteur puisqu'on mélange
"ce que fait" la classe avec "comment elle le fait". En raison de ce mélange, on préfère normalement définir des fonctions membres
en dehors du corps de classe avec le mot-clé inline.
Comprenez que dans un monde orienté objet et réutilisation, il y a beaucoup de
gens qui utilisent la classe, mais une seule personne qui la crée (vous même).
C'est pourquoi vous devriez faire les choses en faveur du
plus grand nombre plutôt que pour le plus petit.
|
Consultez les autres F.A.Q.
|
|