Definition / Declaration ************************ .. include:: ../BoutonGoogleTrad.rst Le langage C++ s'est construit au fil du temps et pas toujours dans un souci d'harmonisation. Ainsi ce chapitre qui aborde les déclarations et les définitions en C++ amène tout un lot de cas particuliers et de dérogations liées à certains mots-clefs. Nous avons volontairement choisi comme pédagogie de mettre l'accent sur quelques grands principes. Pour cela, nous nous plaçons dans un cadre idéal d'un programme comportant **un seul fichier source sans aucune inclusion**. Principe général ================ Dans cette page, nous choisissons de désigner par le terme *entité* : une variable, une fonction, une structure... Définition et déclaration en C++ -------------------------------- .. warning:: Dans l'univers du C++, déclaration et définition sont deux notions proches mais cependant différentes. Faites très attention, car dans d'autres langages, ces deux termes sont souvent utilisés de manière équivalente. D'après la `terminologie du langage C++ `_, une **déclaration** (:cppterm:`declaration`) sert à : * Introduire ou réintroduire une entité dans un programme en rappelant son type. D'après `cet autre lien `_, une **définition** (:cppterm:`definition`) correspond à : * Une déclaration * Accompagnée de suffisamment d'informations pour que le compilateur puisse créer cette entité. .. note:: On constate qu'une définition est également une déclaration. Voici une règle commune à de nombreux langages : .. panels:: :column: col-lg-10 p-2 **Règle** :cppterm:`DBU (Declaration Before Use)` : une entité doit être déclarée (ou définie) avant de pouvoir être utilisée Dans le langage C++, le principe intitulé `One Definition Rule `_ s'applique : .. panels:: :column: col-lg-10 p-2 **Règle** :cppterm:`ODR (One Definition Rule)` : dans un fichier source, on doit trouver une et une seule définition par entité .. note:: La règle ODR concerne les définitions. Cependant, il n'y a pas de limite sur le nombre de déclarations associées à une même entité. Exercice -------- .. quiz:: IntroDefDecla :title: Définition & Déclaration **Dans le cas d'un unique fichier source présent dans le projet**, indiquez si les affirmations suivantes sont vraies ou fausses : #) :quiz:`{"type":"TF","answer":"F"}` En C++, définition et déclaration signifient la même chose. #) :quiz:`{"type":"TF","answer":"F"}` La règle :cpp:`ODR` indique qu'il faut que chaque entité soit définie au moins une fois durant la compilation. #) :quiz:`{"type":"TF","answer":"T"}` Une déclaration introduit ou réintroduit un nom et un type. #) :quiz:`{"type":"TF","answer":"T"}` On peut trouver plusieurs déclarations d'une même entité dans un fichier source. #) :quiz:`{"type":"TF","answer":"F"}` Si une déclaration est présente, la définition devient optionnelle. #) :quiz:`{"type":"TF","answer":"F"}` On peut utiliser un nom n'importe où dans le fichier source sans risque d'erreur. Les variables ============= Vous pouvez retrouver une `présentation sur les variables `_ dans la référence sur le C++. Syntaxe ------- .. panels:: :column: col-lg-10 p-2 **Définition d'une variable :** #) :cpp:`Type Name; // without initialization` #) :cpp:`Type Name = InitValue; // with initialization` .. panels:: :column: col-lg-10 p-2 **Déclaration d'une variable** Hors programme Définir une variable sans l'initialiser est risquée et doit être réservée à de très rares occasions. .. note:: Les syntaxes à base d'accolades : :cpp:`int a = {40};` ou :cpp:`int a{40};` ou :cpp:`int a = { };` ou :cpp:`int a{ };` sont hors programme. Exemple ------- .. code-block:: cpp int anInteger; // definition of an integer variable without initialization double aFloatingNumber = 3.0; // definition of the variable aFloatingNumber of type double with initialization Initialisation des variables par défaut --------------------------------------- Commençons par revenir sur une idée fausse mais pourtant très répandue chez les élèves. .. # define a hard line break for HTML .. |br| raw:: html
.. panels:: :column: col-lg-10 p-2 NON, les variables ne sont pas initialisées par défaut en C++. |br| NON, les variables numériques ne sont pas initialisées à 0 en C++. |br| OUI, une variable locale non initialisée contient une valeur indéterminée. Vérifions cette affirmation au moyen du programme suivant : .. code-block:: cpp #include int main() { int a; std::cout<> 0 Attention, pas de conclusion attive ! La mémoire de votre programme est généralement mise à zéro par l'OS pour des raisons de sécurité/confidentialité. Ainsi, en début d'exécution, une variable non initialisée a souvent de grande chance de valoir 0. Mais la raison n'a rien à voir avec le C++. Prenons cet autre exemple : .. code-block:: cpp int main() { for (int i = 0; i < 100 ; i++) { int a; a += 1; std::cout<> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35... Cette exemple montre que la variable :cpp:`a` réutilise la case mémoire de la précédente variable :cpp:`a`. Comme le C++ n'initialise pas les variables par défaut, on voit dériver la valeur de :cpp:`a`, ce qui confirme qu'une variable non initialisée contient une valeur indéterminée. .. warning:: L'utilisation d'une variable non initialisée ne produit pas de message d'erreur. Il est possible de forcer le compilateur à déclencher une erreur en utilisant les paramètres de compilation supplémentaires :cpp:`-Wall -Werror`. .. panels:: :column: col-lg-10 p-2 Convention : tant qu'une variable n'est pas initialisée, il ne faut pas l'utiliser car son contenu est indéterminée. Masquage -------- Pour une variable, la règle ODR s'applique à l'intérieur du bloc d'accolades où elle a été créée. Cependant, dans un sous-bloc d'accolades, vous pouvez redéfinir une variable portant le même nom qu'une variable précédente. Dans ce cas, la variable dans ce sous-bloc masque la précédente (:cppterm:`variable shadowing`): .. code-block:: cpp #include int main() { int x = 2; { int x = 3; // this new variable x hides the one from the outer block std::cout << x << " "; } return 0; } >> 3 **Cette situation est à éviter** car elle nuit fortement à la lisibilité/compréhension du programme. Exemples d'erreurs ------------------ L'utilisation d'une variable avant toute définition produit une erreur de la forme : le nom n'est pas déclaré. Voici un exemple : .. code-block:: cpp #include int main() { std::cout << a; return 0; } Le redéfinition d'une variable, même à l'identique, produit une erreur de compilation de la forme : erreur de redéclaration. Voici un exemple : .. code-block:: cpp int main() { int a = 2; int a = 2; return 0; } .. panels:: Exercice 1 .. code-block:: cpp :linenos: a = 0; int a; --- Exercice 2 .. code-block:: cpp :linenos: int a = 2; a = 4; --- Exercice 3 .. code-block:: cpp :linenos: int a; int b = c; int c; --- Exercice 4 .. code-block:: cpp :linenos: int a = 2; int i = 3; int c; int d = a + j; .. quiz:: Reglesdutilisation :title: Règles d'utilisation Le compilateur parcourt le code source de haut en bas. Dès qu'il rencontre un problème : identificateur inconnu, variable non initialisée... il s'arrête et envoie un message d'erreur. Pour chaque exemple, indiquez la ligne où se trouve l'erreur ou 0 si tout est correct : .. csv-table:: :delim: ! Exercice 1 :quiz:`{"type":"FB","answer":"1"}` ! Exercice 2 :quiz:`{"type":"FB","answer":"0"}` Exercice 3 :quiz:`{"type":"FB","answer":"2"}` ! Exercice 4 :quiz:`{"type":"FB","answer":"4"}` .. quiz:: IntroDefDeclaBis :title: Définition de variables Indiquez si les affirmations suivantes sont vraies ou fausses : #) :quiz:`{"type":"TF","answer":"T"}` La définition d'une variable donne son nom et son type. #) :quiz:`{"type":"TF","answer":"T"}` Le compilateur C++ accepte d'utiliser une variable non initialisée. #) :quiz:`{"type":"TF","answer":"F"}` En C++, les numériques sont automatiquement initialisées à la valeur 0. #) :quiz:`{"type":"TF","answer":"F"}` Deux définitions du même nom sont possibles si les types sont identiques. #) :quiz:`{"type":"TF","answer":"T"}` On peut utiliser une variable après sa définition. Les fonctions ============= Syntaxe ------- La `spécification du C++ sur les fonctions `_ nous informe que : * La **déclaration d'une fonction** permet d'indiquer son nom et son type (type de retour et type des arguments) * La **définition d'une fonction** est une déclaration suivie d'un **corps de fonction** (:cppterm:`function body`) délimité par une paire d'accolades :cpp:`{ }` et contenant l'ensemble des traitements effectués par cette fonction. .. panels:: :column: col-lg-10 p-2 **Déclaration d'une fonction :** :cpp:`ReturnType FunctionName(Type1 Parameter1, Type2 Parameter2);` .. panels:: :column: col-lg-10 p-2 **Définition d'une fonction :** | :cpp:`ReturnType FunctionName(Type1 Parameter1, Type2 Parameter2)` | :cpp:`{` | :cpp:`...` | :cpp:`}` .. note:: Les fonctions ne retournant pas de valeur doivent retourner le type :cpp:`void`. Déclaration anticipée --------------------- Nous présentons la technique de (:cppterm:`forward declaration`) dans le cadre des fonctions, car c'est dans ce cadre qu'elle est la plus utilisée. Sans déclaration, si nous avons trois fonctions :cpp:`C()` appelant :cpp:`B()` appelant :cpp:`A()`, suivant la règle du :cpp:`declaration before use`, nous devons dans notre code mettre ces trois fonctions exactement dans cet ordre : .. code-block:: cpp void A() { } void B() { A(); } void C() { C(); } Cela est contraignant pour le programmeur. Cependant, l'ajout de déclarations anticipées (avant la définition) retire cette contrainte. Les définitions des fonctions :cpp:`A()`, :cpp:`B()`, :cpp:`C()` peuvent maintenant être faites dans un ordre quelconque. .. code-block:: cpp void A(); // Forward declaration of function A void B(); // Forward declaration of function B void C(); // Forward declaration of function C Exemples d'erreurs ------------------ L'utilisation d'une fonction avant toute définition ou déclaration produit une erreur de la forme : le nom n'est pas déclaré. Voici un exemple : .. code-block:: cpp int main() { F(); // ERROR: "name F is not declared" return 0; } void F() {} Le redéfinition d'une fonction (:cppterm:`function redefinition`), même à l'identique, produit une erreur de compilation de la forme : le nom a déjà été défini. Voici un exemple : .. code-block:: cpp void F() {} void F() {} // ERROR: The name F has already been defined int main() { F(); return 0; } La déclaration d'une fonction sans définition produit une erreur de la forme : aucune définition trouvée .. code-block:: cpp void F(); int main() { F(); return 0; } .. quiz:: IntroDefDeclaFNT :title: Définition et déclaration de fonctions Indiquez si les affirmations suivantes sont vraies ou fausses : #) :quiz:`{"type":"TF","answer":"F"}` En C++, définition et déclaration de fonctions signifient la même chose. #) :quiz:`{"type":"TF","answer":"F"}` Si la fonction ne retourne pas de valeur, son type de retour est absent. #) :quiz:`{"type":"TF","answer":"T"}` Une définition de fonction doit inclure un corps de fonction. #) :quiz:`{"type":"TF","answer":"F"}` Un corps de fonction est délimité par une paire de crochets. #) :quiz:`{"type":"TF","answer":"F"}` Un corps de fonction se termine par un point virgule ; #) :quiz:`{"type":"TF","answer":"T"}` Un appel de fonction sans déclaration préalable déclenche une erreur. #) :quiz:`{"type":"TF","answer":"F"}` La syntaxe d'une déclaration de fonction se termine par une parenthèse ) #) :quiz:`{"type":"TF","answer":"F"}` Si le compilateur ne trouve aucune définition, il n'émet pas d'erreur. #) :quiz:`{"type":"TF","answer":"F"}` Si le compilateur trouve deux définitions identiques dans le même fichier, il n'émet pas d'erreur. :cppterm:`Donnez le terme C++` signifiant *déclaration anticipée* - ne pas utiliser de majuscules : :quiz:`{"type":"FB","answer":"forward declaration"}` Les structures ============== Nous rappelons qu'un type structure (:cppterm:`structure`) est un type composé. Il s'agit d'une **classe** dont tous les membres sont publics par défaut. .. warning:: Attention, définir un type structure nommée :cpp:`S` et définir une variable de type :cpp:`S` sont deux choses différentes. Dans le premier cas, on définit une classe dans l'autre on instancie un objet. Syntaxe ------- On trouve : * La **déclaration d'un type structure** permet de déclarer son nom et son type (:cpp:`structure`) * La **définition d'un type structure** inclut la déclaration suivie d'une liste de variables/fonctions présentes dans la structure. Cette liste est délimitée par une paire d'accolades et se termine par un :cpp:`;` .. panels:: :column: col-lg-10 p-2 **Déclaration d'une structure :** :cpp:`struct Nom;` .. panels:: :column: col-lg-10 p-2 **Définition d'une structure :** | :cpp:`struct Nom` | :cpp:`{` | :cpp:`Type1 var1;` | :cpp:`Type2 fnt(Type3 var2) {...};` | :cpp:`...` | :cpp:`};` .. warning:: Après une déclaration, la structure peut être utilisée de manière très limitée. En effet, aucune information n'est donnée sur les membres internes et il n'est donc pas possible d'y accéder. On peut cependant créer des pointeurs vers cette structure. Après sa déclaration, l’usage d’une structure reste très restreint : ses membres n'étant pas définis, ceci empêche tout accès aux variables et fonctions. Il demeure néanmoins possible de créer des pointeurs vers cette structure. Exemples d'erreurs ------------------ Si nous insérons deux fois une définition dans un même fichier, ceci produit une erreur de redéfinition : .. code-block:: cpp struct A { int x; }; struct A { int x; }; // error: previous definition of struct A int main() { } .. quiz:: IntroDefDeclaStuct :title: Définition et déclaration des types structures Indiquez si les affirmations suivantes sont vraies ou fausses : #) :quiz:`{"type":"TF","answer":"T"}` Un type structure est un type. #) :quiz:`{"type":"TF","answer":"F"}` La syntaxe de la définition d'un type structure se termine par une accolade } #) :quiz:`{"type":"TF","answer":"F"}` Dans un même fichier, on peut définir plusieurs fois le même type structure. Préparation aux examens ======================= Il est vivement recommandé de ne pas déléguer ces exercices à l'IA car ils seront repris lors des examens écrits. Exercice 1 ---------- * Faites en sorte que ce programme compile sans utiliser de déclaration de fonctions. * Pour cela, **vous devez changer l'ordre des lignes**. .. code-block:: cpp #include using namespace std; void grape(){ cout<<"grape"< using namespace std; void piano(){ cout<<"piano"<