Débuter avec C++
Bonjour le monde
Ce programme affiche “Hello World !” dans le flux de sortie standard :
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
A voir en direct sur Coliru.
Une analyse
Examinons chaque partie de ce code en détail :
-
#include <iostream>
est une directive de préprocesseur qui inclut le contenu du fichier d’en-tête C++ standardiostream
.iostream
is a standard library header file that contains definitions of the standard input and output streams. These definitions are included in thestd
namespace, explained below.The standard input/output (I/O) streams provide ways for programs to get input from and output to an external system – usually the terminal.
-
int main() { ... }
définit une nouvelle [fonction][1] nomméemain
. Par convention, la fonction ‘main’ est appelée à l’exécution du programme. Il ne doit y avoir qu’une seule fonctionmain
dans un programme C++, et elle doit toujours renvoyer un nombre de typeint
.Here, the
int
is what is called the function’s [return type][2]. La valeur renvoyée par la fonctionmain
est un code de sortie.By convention, a program exit code of
0
orEXIT_SUCCESS
is interpreted as success by a system that executes the program. Any other return code is associated with an error.If no
return
statement is present, themain
function (and thus, the program itself) returns0
by default. In this example, we don’t need to explicitly writereturn 0;
.All other functions, except those that return the
void
type, must explicitly return a value according to their return type, or else must not return at all. -
std::cout << "Bonjour le monde !" << std::endl;
affiche “Hello World!” au flux de sortie standard :-
std
is a [namespace][3], and::
is the scope resolution operator that allows look-ups for objects by name within a namespace.There are many namespaces. Here, we use
::
to show we want to usecout
from thestd
namespace. For more information refer to [Scope Resolution Operator - Microsoft Documentation][4]. -
std::cout
is the standard output stream object, defined iniostream
, and it prints to the standard output (stdout
). -
<<
is, in this context, the stream insertion operator, so called because it inserts an object into the stream object.The standard library defines the
<<
operator to perform data insertion for certain data types into output streams.stream << content
insertscontent
into the stream and returns the same, but updated stream. This allows stream insertions to be chained:std::cout << "Foo" << " Bar";
prints “FooBar” to the console. -
"Hello World!"
is a character string literal, or a “text literal.” The stream insertion operator for character string literals is defined in fileiostream
. -
std::endl
is a special I/O stream manipulator object, also defined in fileiostream
. Inserting a manipulator into a stream changes the state of the stream.The stream manipulator
std::endl
does two things: first it inserts the end-of-line character and then it flushes the stream buffer to force the text to show up on the console. This ensures that the data inserted into the stream actually appear on your console. (Stream data is usually stored in a buffer and then “flushed” in batches unless you force a flush immediately.)An alternate method that avoids the flush is:
std::cout << "Hello World!\n";
where
\n
is the character escape sequence for the newline character. -
The semicolon (
;
) notifies the compiler that a statement has ended. All C++ statements and class definitions require an ending/terminating semicolon.
-
[1] : https://www.wikiod.com/fr/docs/c%2B%2B/206/getting-started-with-c-language/25460/function [2] : https://www.wikiod.com/fr/docs/c%2B%2B/572/lambdas/1855/specifying-the-return-type#t=20170126184705835537 [3] : https://www.wikiod.com/fr/docs/c%2B%2B/495/namespaces#t=201701261847520776598 [4] : https://msdn.microsoft.com/en-us/library/b451xz31.aspx/ “Opérateur de résolution de portée - Documentation Microsoft”
Commentaires
Un commentaire est un moyen de mettre du texte arbitraire dans le code source sans que le compilateur C++ l’interprète avec une quelconque signification fonctionnelle. Les commentaires sont utilisés pour donner un aperçu de la conception ou de la méthode d’un programme.
Il existe deux types de commentaires en C++ :
Commentaires sur une seule ligne
La double séquence de barre oblique //
marquera tout le texte jusqu’à une nouvelle ligne en tant que commentaire :
int main()
{
// This is a single-line comment.
int a; // this also is a single-line comment
int i; // this is another single-line comment
}
Commentaires de style C/bloc
La séquence /*
est utilisée pour déclarer le début du bloc de commentaire et la séquence */
est utilisée pour déclarer la fin du commentaire. Tout le texte entre les séquences de début et de fin est interprété comme un commentaire, même si le texte est par ailleurs une syntaxe C++ valide. Ceux-ci sont parfois appelés commentaires de “style C”, car cette syntaxe de commentaire est héritée du langage prédécesseur de C++, C :
int main()
{
/*
* This is a block comment.
*/
int a;
}
Dans n’importe quel commentaire de bloc, vous pouvez écrire ce que vous voulez. Lorsque le compilateur rencontre le symbole */
, il termine le commentaire de bloc :
int main()
{
/* A block comment with the symbol /*
Note that the compiler is not affected by the second /*
however, once the end-block-comment symbol is reached,
the comment ends.
*/
int a;
}
L’exemple ci-dessus est un code C++ (et C) valide. Cependant, avoir /*
supplémentaire à l’intérieur d’un commentaire de bloc peut entraîner un avertissement sur certains compilateurs.
Les commentaires de bloc peuvent également commencer et se terminer sur une seule ligne. Par exemple:
void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);
Importance des commentaires
Comme avec tous les langages de programmation, les commentaires offrent plusieurs avantages :
- Documentation explicite du code pour faciliter sa lecture/maintenance
- Explication du but et des fonctionnalités du code
- Détails sur l’historique ou le raisonnement derrière le code
- Placement des droits d’auteur/licences, notes de projet, remerciements spéciaux, crédits des contributeurs, etc. directement dans le code source.
Cependant, les commentaires ont aussi leurs inconvénients :
- Ils doivent être maintenus pour refléter tout changement dans le code
- Les commentaires excessifs ont tendance à rendre le code moins lisible
Le besoin de commentaires peut être réduit en écrivant un code clair et auto-documenté. Un exemple simple est l’utilisation de noms explicatifs pour les variables, les fonctions et les types. La factorisation des tâches logiquement liées en fonctions discrètes va de pair avec cela.
Marqueurs de commentaires utilisés pour désactiver le code
Pendant le développement, les commentaires peuvent également être utilisés pour désactiver rapidement des portions de code sans les supprimer. Ceci est souvent utile à des fins de test ou de débogage, mais ce n’est pas un bon style pour autre chose que des modifications temporaires. Ceci est souvent appelé “commenter”.
De même, conserver les anciennes versions d’un morceau de code dans un commentaire à des fins de référence est mal vu, car cela encombre les fichiers tout en offrant peu de valeur par rapport à l’exploration de l’historique du code via un système de gestion des versions.
Le processus de compilation standard C++
Le code de programme exécutable C++ est généralement produit par un compilateur.
Un compilateur est un programme qui traduit le code d’un langage de programmation en une autre forme qui est (plus) directement exécutable pour un ordinateur. L’utilisation d’un compilateur pour traduire du code s’appelle compilation.
C++ hérite de la forme de son processus de compilation de son langage “parent”, C. Vous trouverez ci-dessous une liste montrant les quatre étapes principales de la compilation en C++ :
- Le préprocesseur C++ copie le contenu de tous les fichiers d’en-tête inclus dans le fichier de code source, génère un code de macro et remplace les constantes symboliques définies à l’aide de #define par leurs valeurs.
- Le fichier de code source étendu produit par le préprocesseur C++ est compilé dans le langage d’assemblage approprié pour la plate-forme.
- Le code assembleur généré par le compilateur est assemblé en code objet approprié pour la plate-forme.
- Le fichier de code objet généré par l’assembleur est lié aux fichiers de code objet pour toutes les fonctions de bibliothèque utilisées pour produire un fichier exécutable.
- Remarque : certains codes compilés sont liés entre eux, mais pas pour créer un programme final. Habituellement, ce code “lié” peut également être empaqueté dans un format qui peut être utilisé par d’autres programmes. Cet “ensemble de code empaqueté et utilisable” est ce que les programmeurs C++ appellent une bibliothèque.
De nombreux compilateurs C++ peuvent également fusionner ou dissocier certaines parties du processus de compilation pour plus de facilité ou pour une analyse supplémentaire. De nombreux programmeurs C++ utiliseront différents outils, mais tous les outils suivront généralement ce processus généralisé lorsqu’ils seront impliqués dans la production d’un programme.
Le lien ci-dessous prolonge cette discussion et fournit un joli graphique pour vous aider. [1] : http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
Fonction
Une fonction est une unité de code qui représente une séquence d’instructions.
Les fonctions peuvent accepter des arguments ou des valeurs et renvoyer une seule valeur (ou non). Pour utiliser une fonction, un appel de fonction est utilisé sur les valeurs d’argument et l’utilisation de l’appel de fonction lui-même est remplacée par sa valeur de retour.
Chaque fonction a une signature de type – les types de ses arguments et le type de son type de retour.
Les fonctions s’inspirent des concepts de procédure et de fonction mathématique.
- Remarque : les fonctions C++ sont essentiellement des procédures et ne suivent pas la définition ou les règles exactes des fonctions mathématiques.
Les fonctions sont souvent destinées à effectuer une tâche spécifique. et peut être appelée à partir d’autres parties d’un programme. Une fonction doit être déclarée et définie avant d’être appelée ailleurs dans un programme.
- Remarque : les définitions de fonctions courantes peuvent être masquées dans d’autres fichiers inclus (souvent pour des raisons de commodité et de réutilisation dans de nombreux fichiers). Il s’agit d’une utilisation courante des fichiers d’en-tête.
Déclaration de fonction
Une déclaration de fonction déclare l’existence d’une fonction avec son nom et sa signature de type au compilateur. La syntaxe est la suivante :
int add2(int i); // The function is of the type (int) -> (int)
Dans l’exemple ci-dessus, la fonction int add2(int i)
déclare ce qui suit au compilateur :
- Le type de retour est
int
. - Le nom de la fonction est
add2
. - Le nombre d’arguments de la fonction est 1 :
- Le premier argument est du type
int
. - Le premier argument sera référencé dans le contenu de la fonction par le nom
i
.
Le nom de l’argument est facultatif ; la déclaration de la fonction pourrait également être la suivante :
int add2(int); // Omitting the function arguments' name is also permitted.
Selon la règle de définition unique, une fonction avec une certaine signature de type ne peut être déclarée ou définie qu’une seule fois dans une base de code C++ entière visible par le compilateur C++. En d’autres termes, les fonctions avec une signature de type spécifique ne peuvent pas être redéfinies – elles ne doivent être définies qu’une seule fois. Ainsi, ce qui suit n’est pas valide en C++ :
int add2(int i); // The compiler will note that add2 is a function (int) -> int
int add2(int j); // As add2 already has a definition of (int) -> int, the compiler
// will regard this as an error.
Si une fonction ne renvoie rien, son type de retour est écrit void
. S’il ne prend aucun paramètre, la liste des paramètres doit être vide.
void do_something(); // The function takes no parameters, and does not return anything.
// Note that it can still affect variables it has access to.
Appel de fonction
Une fonction peut être appelée après avoir été déclarée. Par exemple, le programme suivant appelle add2
avec la valeur de 2
dans la fonction de main
:
#include <iostream>
int add2(int i); // Declaration of add2
// Note: add2 is still missing a DEFINITION.
// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.
int main()
{
std::cout << add2(2) << "\n"; // add2(2) will be evaluated at this point,
// and the result is printed.
return 0;
}
Ici, add2(2)
est la syntaxe d’un appel de fonction.
Définition de la fonction
Une définition de fonction* est similaire à une déclaration, sauf qu’elle contient également le code qui est exécuté lorsque la fonction est appelée dans son corps.
Un exemple de définition de fonction pour add2
pourrait être :
int add2(int i) // Data that is passed into (int i) will be referred to by the name i
{ // while in the function's curly brackets or "scope."
int j = i + 2; // Definition of a variable j as the value of i+2.
return j; // Returning or, in essence, substitution of j for a function call to
// add2.
}
Surcharge de fonctions
Vous pouvez créer plusieurs fonctions avec le même nom mais des paramètres différents.
int add2(int i) // Code contained in this definition will be evaluated
{ // when add2() is called with one parameter.
int j = i + 2;
return j;
}
int add2(int i, int j) // However, when add2() is called with two parameters, the
{ // code from the initial declaration will be overloaded,
int k = i + j + 2 ; // and the code in this declaration will be evaluated
return k; // instead.
}
Les deux fonctions sont appelées par le même nom “add2”, mais la fonction réelle appelée dépend directement de la quantité et du type des paramètres de l’appel. Dans la plupart des cas, le compilateur C++ peut calculer quelle fonction appeler. Dans certains cas, le type doit être explicitement indiqué.
Paramètres par défaut
Les valeurs par défaut des paramètres de fonction ne peuvent être spécifiées que dans les déclarations de fonction.
int multiply(int a, int b = 7); // b has default value of 7.
int multiply(int a, int b)
{
return a * b; // If multiply() is called with one parameter, the
} // value will be multiplied by the default, 7.
Dans cet exemple, multiply()
peut être appelé avec un ou deux paramètres. Si un seul paramètre est donné, b
aura la valeur par défaut de 7. Les arguments par défaut doivent être placés dans les derniers arguments de la fonction. Par exemple:
int multiply(int a = 10, int b = 20); // This is legal
int multiply(int a = 10, int b); // This is illegal since int a is in the former
Appels de fonctions spéciales - Opérateurs
Il existe des appels de fonctions spéciales en C++ qui ont une syntaxe différente de name_of_function(value1, value2, value3)
. L’exemple le plus courant est celui des opérateurs.
Certaines séquences de caractères spéciaux qui seront réduites à des appels de fonction par le compilateur, telles que !
, +
, -
, *
, %
et <<
et bien d’autres. Ces caractères spéciaux sont normalement associés à une utilisation hors programmation ou sont utilisés pour l’esthétique (par exemple, le caractère “+” est communément reconnu comme le symbole d’addition à la fois dans la programmation C++ et dans les mathématiques élémentaires).
C++ gère ces séquences de caractères avec une syntaxe spéciale ; mais, par essence, chaque occurrence d’un opérateur est réduite à un appel de fonction. Par exemple, l’expression C++ suivante :
3+3
est équivalent à l’appel de fonction suivant :
operator+(3, 3)
Tous les noms de fonction d’opérateur commencent par operator
.
Alors que dans le prédécesseur immédiat de C++, C, les noms de fonctions d’opérateur ne peuvent pas se voir attribuer des significations différentes en fournissant des définitions supplémentaires avec des signatures de type différentes, en C++, cela est valide. “Masquer” des définitions de fonctions supplémentaires sous un nom de fonction unique est appelé surcharge d’opérateur en C++, et est une convention relativement courante, mais pas universelle, en C++.
[1] : https://i.stack.imgur.com/q2JUv.png [2] : https://i.stack.imgur.com/1l105.png [3] : https://i.stack.imgur.com/u8Zwk.png
Visibilité des prototypes de fonctions et des déclarations
En C++, le code doit être déclaré ou défini avant utilisation. Par exemple, ce qui suit génère une erreur de compilation :
int main()
{
foo(2); // error: foo is called, but has not yet been declared
}
void foo(int x) // this later definition is not known in main
{
}
Il y a deux façons de résoudre ce problème : mettre la définition ou la déclaration de foo()
avant son utilisation dans main()
. Voici un exemple :
void foo(int x) {} //Declare the foo function and body first
int main()
{
foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
Cependant, il est également possible de “déclarer en avant” la fonction en plaçant uniquement une déclaration “prototype” avant son utilisation, puis en définissant le corps de la fonction ultérieurement :
void foo(int); // Prototype declaration of foo, seen by main
// Must specify return type, name, and argument list types
int main()
{
foo(2); // OK: foo is known, called even though its body is not yet defined
}
void foo(int x) //Must match the prototype
{
// Define body of foo here
}
Le prototype doit spécifier le type de retour (void
), le nom de la fonction (foo
) et les types de variables de liste d’arguments (int
), mais les [noms des arguments ne sont PAS requis][1] .
Une façon courante d’intégrer cela dans l’organisation des fichiers source consiste à créer un fichier d’en-tête contenant toutes les déclarations de prototype :
// foo.h
void foo(int); // prototype declaration
puis fournissez la définition complète ailleurs:
// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
puis, une fois compilé, liez le fichier objet correspondant foo.o
dans le fichier objet compilé où il est utilisé dans la phase de liaison, main.o
:
// main.cpp --> main.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files
Une erreur “symbole externe non résolu” se produit lorsque la fonction prototype et call existent, mais que la fonction body n’est pas définie. Celles-ci peuvent être plus difficiles à résoudre car le compilateur ne signalera pas l’erreur avant la dernière étape de liaison et il ne sait pas à quelle ligne sauter dans le code pour afficher l’erreur.
[1] : https://stackoverflow.com/questions/5234169/why-do-function-prototypes-include-parameter-names-when-theyre-not-required
Préprocesseur
Le préprocesseur est une partie importante du [compilateur.][1]
Il édite le code source, en supprimant certains bits, en en modifiant d’autres et en ajoutant d’autres éléments.
Dans les fichiers source, nous pouvons inclure des directives de préprocesseur. Ces directives indiquent au préprocesseur d’effectuer des actions spécifiques. Une directive commence par un # sur une nouvelle ligne. Exemple:
#define ZERO 0
La première directive de préprocesseur que vous rencontrerez est probablement la
#include <something>
directif. Ce qu’il fait, c’est prendre tout “quelque chose” et l’insérer dans votre fichier où se trouvait la directive. Le programme [hello world][2] commence par la ligne
#include <iostream>
Cette ligne ajoute les [fonctions][3] et les objets qui vous permettent d’utiliser l’entrée et la sortie standard.
Le langage C, qui utilise également le préprocesseur, n’a pas autant de [fichiers d’en-tête][4] que le langage C++, mais en C++, vous pouvez utiliser tous les fichiers d’en-tête C.
La prochaine directive importante est probablement la
#define something something_else
directif. Cela indique au préprocesseur qu’au fur et à mesure qu’il progresse dans le fichier, il doit remplacer chaque occurrence de something
par something_else
. Il peut également faire des choses similaires aux fonctions, mais cela compte probablement comme du C++ avancé.
Le something_else
n’est pas nécessaire, mais si vous définissez something
comme rien, alors en dehors des directives du préprocesseur, toutes les occurrences de something
disparaîtront.
Ceci est en fait utile, à cause des directives #if
,#else
et #ifdef
. Le format de ceux-ci serait le suivant :
#if something==true
//code
#else
//more code
#endif
#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
Ces directives insèrent le code qui est dans le vrai bit et suppriment les faux bits. cela peut être utilisé pour avoir des morceaux de code qui ne sont inclus que sur certains systèmes d’exploitation, sans avoir à réécrire tout le code.
[1] : https://www.wikiod.com/fr/docs/c%2B%2B/8209/the-c-compilation-process#t=201702151617103019866 [2] : https://www.wikiod.com/fr/docs/c%2B%2B/206/getting-started-with-c-language/774/hello-world [3] : https://www.wikiod.com/fr/docs/c%2B%2B/206/getting-started-with-c-language/25460/function [4] : https://www.wikiod.com/fr/docs/c%2B%2B/7211/header-files#t=20170215162302552333