Plan du site  
pixel
pixel

Articles - Étudiants SUPINFO

Le C++ en bref Part.2

Par Franck MOURET Publié le 31/10/2016 à 10:52:56 Noter cet article:
(0 votes)
Avis favorable du comité de lecture

Introduction

Lors de la première partie, nous avons vu ce qu’il en était des variables et des opérateurs et ce que l’on pouvait faire en le mélangeant en les associant entre eux. Nous avons également décortiqué un peu le fonctionnement du programme « Hello World ». Ces explications nous ont permis de tester ce que nous avons vu par la suite au sujet des variables.

Dans cette partie, nous allons finir de voir quelques éléments sur les opérateurs et passer à la suite qui va concerner différents éléments, qui vont permettre d’aller plus loin dans la conception de nos programmes.

Opérateur ternaire :

L’opérateur ternaire se définit par le caractère : « ? ». Sa syntaxe est :

condition ? result1 : result2

Il permet de d’écrire une condition if/else sur une seule ligne. Je ne dirais pas qu’en terme de performance, cette instruction est plus rapide. En tout cas, elle peut rendre un peu plus difficile la lisibilité du code pour un personne peu familière de ce type de syntaxe.

Le résultat renvoyé par ces opérations est de type booléen et les deux conditions évaluées doivent être de même type.

Si la condition est vrai, alors on va renvoyer "true", sinon on renvoie "false".

Opérateurs de conversions de type explicite :

C++ permet de convertir en type explicite à l'aide d'une syntaxe similaire à la syntaxe de l'appel de fonction. Il existe en C ++ plusieurs façons de procéder. La plus simple, est celle qui est héritée du langage C, ou le nouveau type enfermé entre parenthèses () précède le nom de la variable :

Int i ;

Float j = 5.7 ;

i = (int) j ;

Ce code convertit le nombre à virgule 5.7 en une valeur entière : 5. Le reste est perdu. Ici, l'opérateur était de type (int). Une autre façon de faire la même chose est de définir cette fois la variable entre les parenthèses.

#include <iostream>

int main ()

{

int i;

float j = 5.7;

i = int (j);

std::cout << i << "\n";

}

Il est temps maintenant d’entrer un peu plus dans le vif du sujet et de nous attaquer aux différents éléments permettant de construire nos programmes.

Les fonctions:

Les fonctions sont utiles car elles permettent de découper un programme en plusieurs parties individuelles. Cela présente plusieurs avantages. Le premier étant que l’on gagne en clarté dans la lecture du code. Au lieu d’avoir un pavé indigeste ou se retrouve avec plusieurs petits éléments possédant chacun une fonction. Le fait d’avoir plusieurs petits éléments est utile lors de la phase de développement. Cela permet d’avancer par petit bout et du coup on peut valider les étapes au fur et à mesure de la construction du projet.

Les fonctions servent à regrouper une ou plusieurs instructions que l’on veut utiliser pour réaliser une action. Par exemple, ouvrir le fichier test.txt et retourner son contenu. Le gros avantage d’une fonction, c’est qu’elle est réutilisable. On pourra l’appeler à plusieurs moments de notre programme.

A sa création une fonction va se prototyper de la manière suivante :

type identificateur(typeParamètres) ;

Et se définit (ou se déclare) de la manière suivante :

type identificateur(paramètres) { … }

Le prototype se place en début de programme, au-dessus de la fonction main, il est là pour fournir la description d’une fonction définie plus loin. Contrairement à la fonction, le prototype ne contient pas les instructions, ni les noms des paramètres (si on veut, il est possible de les ajouter, mais cela reste facultatif). Ce qui va donner :

void addNumber(int);

Le code est lu de haut en bas, si on fait référence à une fonction qui n’a pas encore était lue, le programme va s’interrompre. C’est pourquoi les prototypes sont utiles, car ils permettent de dire au programme que la fonction existe et qu’il va la trouver un peu plus loin, ce qui lui permet de continuer son exécution.

Utilisation :

Les fonctions sont des blocs de code servant à structurer notre programme. Les fonctions peuvent recevoir des arguments, et en retourner. Lorsque que l’on passe des arguments à notre fonction, les types envoyés doivent correspondre à ceux acceptés par la fonction. Si les types ne correspondent pas, le programme va retourner une erreur.

#include <iostream>

int addition (int, int);

int main()

{

int a = 5;

int b = 8;

int result;

result = addition (a, b);

std::cout << result;

return 0;

}

int addition (int a, int b)

{

int total;

total = a + b;

return total;

}

Une petite remarque au passage, ici les arguments passés à la fonction ont le même nom au moment de l’envoie et de la réception : addition (a, b) et int addition (int a, int b). Que leurs noms soient différents ne posent aucun problème, dans la mesure ou les types sont respectés. Ainsi, le passage de addition (a, b) et int addition (int maPremierVariable, int maSecondeVariable) fonction. Pour une question de clarté, on préférera pour le moment garder les mêmes noms.

Return :

Dans l’exemple ci-dessus, on peut voir que les fonctions se terminent par un « return ». C’est de cette manière que l’on signale que notre fonction se termine. Cette instruction permet de retourner une valeur, sauf si le type de notre fonction est void. Dans le cas où l’on essaye de retourner une valeur, le compilateur nous signalera une erreur nous expliquant que cela n’est pas possible. La raison est que le type void est un type vide, il ne peut donc rien retourner.

Une instruction « return » peut comme dans notre exemple retourner une variable, mais il est aussi possible de lui passer un chiffre. On peut aussi si on le souhaite lui faire retourner une expression. Par exemple au lieu d’avoir une fonction comme nous l’avons écrite, elle pourrait s’écrire :

int addition (int a, int b)

{

return (a + b);

}

Un autre point important concernant cette instruction, c’est qu’elle ne peut pas retourner un type différent du type de notre fonction. Si l’on remplace int addition (int a, int b) par char addition (int a, int b), une erreur se produira.

Les attributs :

Ce sont ce que l’on appelle plus communément des variables ou propriétés. Pour reprendre l’exemple de la voiture, mes attributs sont sa couleur, la puissance de son moteur, je peux avoir une voiture rouge de 60 chevaux et une autre grise de 90 chevaux.

Un attribut se définit comme cela :

int puissance = 90;

Les Méthodes :

Ce que l’on désigne par méthode est en fait une fonction qui va s’appliquer à l’objet et à ses attributs : son comportement. Par exemple : une voiture peut accélérer, tourner, s’arrêter. Si je reprends ma classe créé tout à l’heure on aura :

void accelerer() { }

Les fonctions sont différentes des méthodes. Une fonction est une fonction alors qu’une méthode est une fonction associée à un objet.

Les structures :

La structure de données est une « super variable », elle permet de regrouper plusieurs valeurs. Ces valeurs peuvent avoir différents types et longueurs et sont nommé champ d’une structure.

struct voiture_t {

int année ;

double prix ;

} ;

Cet exemple déclare une structure de type voiture, comportant deux éléments qui possèdent des types différents. Une fois la structure déclarée, on peut l’utiliser très simplement en lui déclarant une variable.

#include <iostream>

#include <string>

#include <stream>

using namespace std;

struct voiture_t {

string marque;

int annee;

} ;

int main () {

voiture_t voiture;

voiture.marque = "Ford";

voiture.annee = 1968;

cout << "Ma voiture est une :\n ";

cout << voiture.marque;

cout << " de " << voiture.annee << "\n";

return 0;

}

Encapsulation :

L’utilisation de le POO va nous imposer d’utiliser ce principe. Lorsque l’on parle d’encapsulation, cela veut dire que l’on restreint ou empêche l’accès direct aux données d’un objet. Le but de cette restriction est d’assurer la sécurité, afin que certaines données ne soient pas modifiées. Si l’on veut pouvoir accéder à ces éléments « protégés », on devra passer par l’intermédiaire de ses méthodes. Ce filtrage va s’effectuer avec la définition d’un niveau de visibilité. Il en existe trois différents :

- Public : les attributs sont accessibles de partout, les classes fille et externe voient les données.

- Private : ces attributs ne sont accessibles que par la classe elle-même, les classes filles et externes ne voient rien.

- Protected : ces attributs ne sont accessibles que par la classe elle-même et ses classes dérivées (fille), les classes externes ne peuvent pas accéder à ces données.

Par défaut, tous les membres (attributs) d’une classe sont définis en privée. Si l’on veut changer cette définition, il faut les précéder du mot clef : public. Ce qui nous donne le code suivant :

public :

{

int a ;

int b ;

}

Héritage :

Il s’agit d’un concept important dans la POO. C’est avec lui que l’on va pouvoir définir une nouvelle classe en se basant sur une classe déjà existante. On peut réutiliser ces méthodes et attributs et en ajouter de nouveaux. Ces ajouts se feront dans la nouvelle classe que l’on va créer.

L’héritage est pratique dans la mesure où l’on peut créer une classe base générique qui va nous donner des matériaux de base, puis on va créer des classes plus spécifiques en fonction des besoins. L’héritage va se définir de la manière suivante :

class voiture { }

class voitureUtilitaire : public voiture { }

Le mot clef public est placé devant le nom de la classe, car en l’absence de définition, elle est considérée comme public par défaut.

Le C++ est capable de gérer l’héritage multiple, mais son utilisation reste de manière générale assez marginale. La faible utilisation de cette technique réside vient de la complexité de sa mise en place. La plupart des programmeurs préféreront utiliser un héritage simple plutôt que de se perdre dans cette mise en place.

Ce que l’on veut dire lorsque l’on parle de classe dérivée, c’est que cette dernière peut hériter de plusieurs classes de base, ce qui lui permet d’utiliser les méthodes et attributs de toutes les classes dont elle hérite. Pour pouvoir effectuer ce genre d’actions, on va déclarer nos classes les unes à la suite des autres.

Les structures de contrôle :

A son exécution, le programme lit le code en partant du début si tout se passe bien jusqu’à la fin. Cela ne veut pas dire pour autant que son exécution en fera de même. Au cours du déroulement de ce programme, plusieurs éléments influx sur le chemin que prennent les données. En fonction des choix que le programmes doit effectuer, son parcours peut être différent d’une exécution à l’autre. Parmi ces différents modificateurs de chemins, il y en a un qui va nous intéresser plus particulièrement ici, ce sont les instructions de contrôle.

Ces instructions existent dans tous les langages on a plus ou moins accès aux mêmes instructions. Je dis plus ou moins, car certaines instructions peuvent exister dans un langage et pas dans un autre. En ce qui concerne le C++ nous avons les possibilités suivantes :

- Les instructions permettant des choix avec les instructions if…else et le switch.

- Les instructions permettant de boucler sur une instructions do…while, while et le for.

Blocs d’instructions :

Ces instructions effectuent leur traitement dans ce que l’on appelle un bloc d’instruction. A l’intérieur de ces blocs, sont regroupées une ou plusieurs instructions qui vont être exécuter. Ces instructions sont encadrées par des accolades ouvrantes et fermantes. C’est entre ces deux accolades que s’exécute notre code.

Par exemple :

{

int a ;

int b ;

}

Pour le moment, cela ne sert pas à grand-chose que de déclarer nos variables entre deux accolades. Et sous cette forme, la code ne compilera pas, car il lui manque une quelque chose lui permettant de lancer ce block, comme par exemple un choix.

Instructions de choix :
If…else / if…else if :

La syntaxe du if se présente de la manière suivante :

If (condition) {instruction}

La structure de contrôle if permet l’exécution du code, tant que la condition est vraie. Cette structure convertit implicitement son expression en booléen. Ce qui veut dire que si la condition est vraie alors il exécute l’instruction.

int a = 5;

if (a = 5) {

cout << « Hello »;

}

Que l’on l’inscrive ou non, une condition if comporte toujours un else qui est son comportement dans le cas ou notre condition est fausse. Dans le cas ou a ne vaut pas 5, il faut bien qu’il se passe quelque chose (un comportement pas défaut), et autant contrôler ce qui se passe dans ce cas-là. A ce moment-là notre syntaxe sera :7

if (condition) {instruction1}

else {instruction2}

Comme on peut le voir, else ne possède pas de condition. Mais peut définir un comportement à adopter si la condition principale venait à ne plus être vraie.

Il est bien évidement possible d’imbriquer plusieurs conditions les unes dans les autres. On fera toutefois attention de ne pas vouloir imbriquer plus de 4 ou 5 conditions.

if ( a <= 4) {

if (b = 5) {

std::cout << « Hello »;

} else {

std::cout << « Error. »;

} else {}

Pour accéder à la deuxième condition, la valeur de “a” doit être inférieur ou égal à 4. Si « b » est égal à 5 alors le message « Hello » va s’afficher. Dans le cas contraire, il retourne « Error ».

Le switch :

Une autre manière de faire des choix est l’utilisation de l’instruction switch. Cette instruction prend une expression et va évaluer les différentes valeurs jusqu’à en rencontrer une qui soit vraie (s’il y en a une), sinon, comme pour le if, l’instruction prend un comportement par défaut. La construction de la syntaxe du switch est un peu particulière puisqu’elle s’écrit de la manière suivante :

switch (expression) {

case constante1 :

instruction ;

Break ;

case constante2 :

instruction ;

Break ;

Default:

instruction ;

Break ;

}

L’emploi du mot clef break est important, car sinon le programme va passer toutes les conditions, sans jamais s’arrêter sur aucune et se terminer sur défaut.

En commençant cette partie, je vous ai dit qu’il existait deux types d’instructions, certaines permettant d’effectuer des choix, ce que nous venons de voir ici et d’autres, permettant de boucler celles que nous allons voir maintenant.

Instructions de boucles :

Ces instructions, vont nous permettre d’exécuter des instructions jusqu’à ce que notre condition soit vraie.

Le do/while :

Syntaxe :

do instruction ;

while (expresssion);

Cette instruction va s’exécuter plusieurs fois jusqu’à ce que son expression arrive à zéro. La particularité de cette expression est qu’elle est toujours parcourue au moins une fois. Cela vient du fait que contrairement aux autres instructions qui commencent par l’évaluation de leur expression, ici cela se fait en denier. Il faut bien faire attention à un petit piège, puisque cette instruction se termine par un point-virgule après le while.

While :

L’instruction conditionnel while effectue une boucle tant que la condition est vraie. A chaque exécution de la boucle, tout ce qui se trouve en instruction est exécutée. Nous avons vu que l’instruction do…while s’exécutait au moins une fois, ici ce n’est pas le cas, puisque l’expression examinée au début de la boucle.

Syntaxe :

while (expression )

{instruction}

Exemple:

int a = 0;

while (a > 5) {

a++;

}

Dans ce cas, a vaut 0.

Tant que cette condition est fausse, "a" sera incrémenté de 1, jusqu’à valoir 5.

Le for :

Syntaxe :

for ([instruction_Init] ; [condition] ; [instruction_suivante] )

instruction

L’instruction for est un élément que l’on utilise souvent pour créer des compteurs. Les trois parties ont chacunes leur rôle. La première permet d’initialiser la variable directement dans l’instruction, ensuite, la seconde est utilisée pour exprimer la condition à évaluer et la dernière fait l’incrémentation de notre variable. Ce qu’on l’on a l’habitude d’écrire ainsi :

int i ;

i = 0 ;

while ( i < 10 ) {

i++;

}

Peut être remplacé avec l’instruction for par :

int i ;

for (i = 0; i < 10; i++) {}

Ce qui est quand même plus pratique pour parcourir un tableau simple.

Les tableaux :

Un tableau est une série d'éléments du même type placés dans des emplacements de mémoire contigus. On peut par exemple déclarer plusieurs type « int » sans avoir à déclarer plusieurs variables. Et chacune sera accessible avec un identifiant unique.

Par exemple, un tableau contenant 5 valeurs serait représenté de la manière suivante :

Chaque case représente un élément du tableau. Les valeurs que l’on peut associer à ce tableau peuvent être de type char, int, float, etc… Les index de ce tableau vont de 0 à 4. Un tableau commence toujours par la valeur zéro. Cela peut paraitre bizarre, mais c’est comme ça. Si l’on veut commencer par la valeur 1 on accèdera à la deuxième case de notre tableau.

Comme pour n’importe quel type de variable, pour pouvoir utiliser un tableau, il faut dans un premier temps le déclarer. La syntaxe pour la déclaration de tableaux de tableaux est la suivante :

type name [elements];

Qui va s’écrire en C++ sous la forme :

int tab [3] ;

Le chiffre entre les crochets correspond au nombre de valeurs maximum que le tableau peut contenir. Ici, donc, 3 éléments maximum.

Initialisation des tableaux :

Pour initialiser un tableau, on peut utiliser deux méthodes. Soit on remplit le tableau avec des valeurs spécifiques qui sont déclarées en dure. Les informations amenées à remplir le tableau sont définies entre les accolades et séparées par des virgules. Le nombre d’entiers insérés dans le tableau doit correspondre au maximum déclaré lors de notre initialisation.

int tab[3] = {77, 33, 88} ;

Chacune de case de notre tableau est maintenant remplies avec une des valeurs ci-dessus. Il est aussi possible de déclarer un tableau et de l’initialiser a avec rien. Dans ce cas-là, les valeurs insérées dans le tableau seront toutes égales à zéro.

int tab[3] = { } ;

Dans la mesure où l’on ne souhaite pas renseigner des valeurs entre les crochets lors de la déclaration du tableau, ce dernier prendra comme valeur le maximum de valeurs qu’il reçoit.

int tab [] = {10,20,30,40,50};

Ce tableau aura donc cinq valeurs.

Accéder aux valeurs d’un tableau :

Pour pouvoir effectuer cette action, on utilise la syntaxe :

name[index]

Reprenons l’exemple de notre tableau à cinq éléments : tab[5] .

L’index d’un tableau commençant par zéro, pour accéder à ma première valeur, il suffit d’écrire :

tab[0]

Ce qui veut dire que l’on veut accéder la dernière valeur de notre tableau, il faut choisir la valeur :

Tab[4]

En choisissant la valeur Tab[5] on accède à la sixième valeur, on est par conséquent sorti du tableau. En C ++, il est syntaxiquement correct de dépasser la plage valide des indices pour un tableau. Cela peut bien entendu créer des problèmes, l’accès à ces éléments externes au tableau peuvent ne pas forcément provoquer d’erreurs au moment de la compilation, mais peuvent en provoquer à l’exécution.

Les tableaux multidimensionnels :

Les tableaux multidimensionnels peuvent être vus comme étant des tableaux de tableaux. Pour déclarer ce type de tableau, il suffit simplement de faire :

int tab[][] ;

Cette image représente un tableau à deux dimensions de 2 par 3 de types int, et qui s’initialise de la façon suivante :

int tab[2][3] ;

Comme pour les tableaux simples, si l’on veut accéder à la valeur 17, il suffit de faire tab[1][0]. Les tableaux multidimensionnels ne sont pas limités à deux indices (à deux dimensions). Ils peuvent contenir autant d'indices que nous en avons besoin. Bien entendu il faut faire attention à la mémoire dont on dispose car la quantité de mémoire nécessaire pour un tableau augmente de façon exponentielle avec chaque dimension.

Passer des tableaux en paramètres :

#include <iostream>

using namespace std;

void printArray (int arg[], int length) {

for (int n=0; n<length; ++n)

cout << arg[n] << ' '; cout << '\n';

}

int main () {

int monPremierTableau[] = {10, 20, 30, 40};

int monSecondTableau[] = {50, 60, 70, 80, 90};

printArray (monPremierTableau,3);

printArray (monSecondTableau,5);

}

La fonction printArray( int arg[]) accepte des tableaux dont les éléments sont de type int, quelle que soit leur longueur. Pour cette raison, nous avons inclus un deuxième paramètre qui indique à la fonction la longueur de chaque tableau que nous passons dans son premier paramètre. Cela permet à la boucle qui va devoir générer le tableau de connaître la taille de notre tableau et lui éviter d’en sortir.

Dans une déclaration de fonction, il est également possible d'inclure des tableaux multidimensionnels. Le format d'un paramètre de tableau tridimensionnel est:

base_type[][depth][depth]

Par exemple, une fonction avec un tableau multidimensionnel comme argument pourrait être:

void procedure (int myarray[][3][4])

Notez que les premiers supports []sont laissés vides, tandis que les suivantes précisent tailles pour leurs dimensions respectives. Cela est nécessaire pour que le compilateur soit en mesure de déterminer la profondeur de chaque dimension supplémentaire.

D'une certaine façon, en passant un tableau comme argument on perd toujours une dimension. La raison est que, pour des raisons historiques, les tableaux ne peuvent pas être directement copiés, et donc ce qui est réellement passé en argument est un pointeur. Ceci est une source commune d'erreurs pour les programmeurs débutants. Bien qu'une compréhension claire des pointeurs sera expliquée dans un chapitre à venir.

Conclusion :

Cette partie fut l’occasion d’aborder plusieurs aspects importants du C++. Certaines des notions vues ici existent dans d’autres langages et suivent le même mode de fonctionnement. Dans la partie qui va suivre, nous allons aborder un autre aspect important du langage, à savoir les classes.

Webographie :

https://fr.wikipedia.org/wiki/C%2B%2B http://alp.developpez.com/tutoriels/debuter-cpp/ https://openclassrooms.com/courses/programmez-avec-le-langage-c/ http://www.cplusplus.com/

Livres :

Pour les nuls – C++

Editions Eyrolles – Programmer en C++

A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
Logo de la société Cisco, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société IBM, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sun-Oracle, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Apple, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sybase, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Novell, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Intel, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Accenture, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société SAP, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Prometric, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Toeic, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo du IT Academy Program par Microsoft, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management

SUPINFO International University
Ecole d'Informatique - IT School
École Supérieure d'Informatique de Paris, leader en France
La Grande Ecole de l'informatique, du numérique et du management
Fondée en 1965, reconnue par l'État. Titre Bac+5 certifié au niveau I.
SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels