Les arguments par défaut
Examinez les deux constructeurs de Stash() dans Stash3.h. Ils ne semblent pas si
différents, n'est-ce pas ? En fait, le premier constructeur semble être un cas spécial du second avec
la taille (size) initiale réglée à zéro. C'est un peu un gaspillage d'efforts de créer et de maintenir
deux versions différentes d'une fonction semblable.
C++ fournit un remède avec les arguments par défaut. Un argument par défaut est une valeur
donnée dans la déclaration que le compilateur insère automatiquement si vous ne fournissez
pas de valeur dans l'appel de fonction. Dans l'exemple Stash(), nous pouvons remplacer
les deux fonctions :
Stash(int size); // Quantitée zéro
Stash(int size, int initQuantity);
par la seule fonction :
Stash(int size, int initQuantity = 0);
La définition de Stash(int) est simplement enlevée -tout ce qui est nécessaire est la
définition unique Stash(int,int).
Maintenant, les deux définitions d'objet :
Stash A(100), B(100, 0);
Produiront exactement les mêmes résultats. Le même constructeur est appelé dans les
deux cas, mais pour A, le deuxième argument est automatiquement substitué par le compilateur
quand il voit que le premier argument est un int et qu'il n'y a pas de second argument.
Le compilateur a vu l'argument par défaut, ainsi il sait qu'il peut toujours faire l'appel à la
fonction s'il substitue ce deuxième argument, ce qui est ce que vous lui avez dit de faire en
lui donnant une valeur par défaut.
Les arguments par défaut sont une commodité, comme la surcharge de fonction est une
commodité. Les deux dispositifs vous permettent d'employer un seul nom de fonction dans
différentes situations. La différence est qu'avec des arguments par défaut le
compilateur ajoute les arguments quand vous ne voulez pas mettre vous-même.
L'exemple précédent est un cas idéal pour employer des arguments par défaut au lieu de la
surcharge de fonction ; autrement vous vous retrouvez avec deux fonctions ou plus qui ont des
signatures semblables et des comportements semblables. Si les fonctions ont des
comportements très différents, il n'est généralement pas logique d'employer des arguments par
défaut (du reste, vous pourriez vous demander si deux fonctions avec des comportements très
différents doivent avoir le même nom).
Il y a deux règles que vous devez prendre en compte quand vous utilisez des arguments par défaut.
En premier lieu, seul les derniers arguments peuvent être mis par défauts. C'est-à-dire que vous ne pouvez pas
faire suivre un argument par défaut par un argument qui ne l'est pas. En second lieu, une
fois que vous commencez à employer des arguments par défaut dans un appel de fonction
particulier, tous les arguments suivants dans la liste des arguments de cette fonction doivent
être par défaut (ceci dérive de la première règle).
Les arguments par défaut sont seulement placés dans la déclaration d'une fonction
(typiquement placée dans un fichier d'en-tête). Le compilateur doit voir la valeur par
défaut avant qu'il puisse l'employer. Parfois les gens placeront les valeurs commentées
des arguments par défaut dans la définition de fonction, pour des raisons de documentation.
void fn(int x /* = 0 */) { // ...
Paramètre fictif
Les arguments dans une déclaration de fonction peuvent être déclarés sans identifiants.
Quand ceux-ci sont employés avec des arguments par défaut, cela peut avoir l'air un peu bizarre. Vous pouvez
vous retrouver avec :
void f(int x, int = 0, float = 1.1);
En C++ vous n'avez pas besoin des identifiants dans la définition de fonction, non plus :
void f(int x, int, float flt) { /* ... */ }
Dans le corps de la fonction, on peut faire référence à x et flt, mais pas à l'argument du
milieu, parce qu'il n'a aucun nom. Cependant, les appels de fonction doivent toujours fournir une
valeur pour le paramètre fictif : f(1) ou f(1.2.3.0). Cette syntaxe
permet de passer l'argument comme un paramètre fictif sans l'utiliser. L'idée est que vous
pourriez vouloir changer la définition de la fonction pour employer le paramètre fictif plus tard,
sans changer le code partout où la fonction est appelée. Naturellement,vous pouvez
accomplir la même chose en employant un argument nommé, mais si vous définissez l'argument
pour le corps de fonction sans l'employer, la plupart des compilateurs vous donneront un
message d'avertissement, supposant que vous avez fait une erreur logique. En omettant
intentionnellement le nom d'argument, vous faite disparaître cet avertissement.
Plus important, si vous commencez à employer un argument de fonction et décidez plus tard
que vous n'en avez pas besoin, vous pouvez effectivement l'enlever sans générer d'avertissements,
et sans pour autant déranger de code client qui appelait la version précédente
de la fonction.