• Aucun résultat trouvé

4.8.1 - Résolution de portée globale

textlines.cleanup();

} ///:~

Ceci est similaire à l'exemple précédent, mais on empile des lignes d'un fichier (sous forme de pointeur de string) sur le Stackpuis on les dépile, ce qui provoque un affichage inversé du fichier à l'écran. Notez que la fonction membre pop( )retourne un void*et que celui-ci doit être casté en string*avant de pouvoir être utilisé. Pour afficher le stringà l'écran, le pointeur est déférencé.

Comme textlinesest rempli, le contenu de lineest “cloné” pour chaque push( )en faisant un new string(line). La valeur retournée par l'expression new est un pointeur sur un nouveau stringqui a été créé et qui a copié l'information dans line. Si vous aviez simplement passé l'adresse de lineà push(), vous auriez obtenu un Stackrempli d'adresses identiques, pointant toutes vers line. Vous en apprendrez plus à propos de ce processus de “clonage” plus tard dans ce livre.

Le nom de fichier est récupéré depuis la ligne de commande. Pour s'assurer qu'il y ait assez d'arguments dans la ligne de commande, vous pouvez voir l'utilisation d'une deuxième fonction issue du fichier d'en-tête require.h:

requireArgs( ), qui compare argcau nombre désiré d'arguments et affiche à l'écran un message d'erreur approprié avant de terminer le programme s'il n'y a pas assez d'arguments.

4.8.1 - Résolution de portée globale

L'opérateur de résolution de portée vous sort des situations dans lesquelles le nom que le compilateur choisit par défaut (le nom “le plus proche”) n'est pas celui que vous voulez. Par exemple, supposez que vous ayiez une structure avec un identificateur local a, et que vous vouliez sélectionner un identificateur global adepuis l'intérieur d'une fonction membre. Le compilateur va par défaut choisir celui qui est local, et donc vous êtes obligé de lui dire de faire autrement. Quand vous voulez spécifier un nom global en utilisant la résolution de portée, vous utilisez l'opérateur avec rien devant. Voici un exemple qui montre une résolution de portée globale à la fois pour une variable et une fonction :

//: C04:Scoperes.cpp // Résolution de portée globale

int a;

void f() {}

struct S { int a;

void f();

};

void S::f() {

::f(); // autrement il y aurait récurrence!

::a++; // Sélectionne le a global a--; // Le a dans la portée du struct }

int main() { S s; f(); } ///:~

Sans la résolution de portée dans S::f( ), le compilateur aurait par défaut sélectionné les versions membres de f( )et a.

4.9 - Résumé

Dans ce chapitre, vous avez appris le “tournant” fondamental du C++: vous pouvez mettre des fonctions dans les structures. Ce nouveau type de structure est appelé un type de données abstraites, et les variables que vous créez en utilisant ces structures sont appelés objets, ou instances, de ce type. Appeler une fonction membre d'un objet est appelé envoyer un messageà cet objet. L'action première en programmation orientée objet est d'envoyer des messages aux objets.

Bien qu'empaqueter les données et les fonctions ensembles apporte un bénéfice considérable à l'organisation du code et simplifie l'utilisation des bibliothèques parce que cela empêche les conflits de noms en les cachant, vous pouvez faire beaucoup plus pour programmer de façon plus sûre en C++. Dans le prochain chapitre, vous apprendrez comment protéger certains membres d'un structpour que vous soyez le seul à pouvoir les manipuler.

Cela établit une frontière claire entre ce que l'utilisateur de la structure peut changer et ce que seul le programmeur peut changer.

4.10 - Exercices

Les solutions de exercices suivants peuvent être trouvés dans le document électronique The Thinking in C++

Annotated Solution Guide, disponible à petit prix sur www.BruceEckel.com.

1 Au sein de la bibliothèque standard du langage C, la fonction puts()imprime un tableau de caractères sur la console (ainsi vous pouvez écrire puts("hello")). Ecrivez un programme C qui utilise puts()mais n'inclut pas

< stdio.h> ou autrement dit déclarez la fonction. Compilez ce programme à l'aide de votre compilateur C.

(Certains compilateurs C++ ne sont pas distincts de leur compilateur C; dans ce cas, vous devez rechercher une option à passer à la ligne de commande qui force une compilation C.) Maintenant, compilez-le avec un compilateur C++ et observez la différence.

2 Créez une déclaration de structure avec une fonction membre unique, puis créez une définition pour cette fonction membre. Créez une instance de votre nouveau type de donnée, et appelez la fonction membre.

3 Modifiez votre solution de l'exercice 2 de telle manière à déclarer la structure dans un fichier d'en-tête

"protégé" de façon adéquate contre les inclusions multiples, avec la définition de la fonction dans un fichier cppet votre main()dans un autre.

4 Créez une structure contenant un membre donnée unique de type int, et deux fonctions globales prenant chacune en argument un pointeur sur cette structure. La première fonction prend un second argument de type intet affecte la valeur de cet argument au membre intde la structure, la seconde affiche la valeur du membre intde cette structure. Testez ces fonctions.

5 Répétez l'exercice 4 mais déplacez les fonctions de manière à ce qu'elles soient des fonctions membres de la structure, et testez à nouveau ces fonctions.

6 Créer une classe qui (de façon redondante) effectue la sélection d'un membre donnée ainsi que l'appel d'une fonction membre en utilisant le mot clé this(qui fait référence à l'adresse de l'objet courant).

7 Créer un Stashqui contient des doubles. Remplissez-le avec 25 valeurs de type double, puis affichez-les sur la console.

8 Répétez l'exercice 7 avec Stack.

9 Créer un fichier contenant une fonction f()qui prend un argument de type intet l'affiche sur la console en utilisant la fonction printf()déclarée dans < stdio.h> en écrivant: printf("%d\n", i)où iest l'entier que vous désirez afficher. Créez un fichier séparé contenant main(), et dans ce fichier, déclarez f()comme prenant un argument de type float. Appelez f()depuis main(). Essayez de compiler et de lier votre programme à l'aide d'un compilateur C++ et observez ce qui se passe. Maintenant compilez et liez ce programme en utilisant un compilateur C, et regardez ce qui se passe lorsqu'il s'exécute. Expliquez les comportements observés.

10 Trouvez comment produire du code assembleur à l'aide de vos compilateurs C et C++. Ecrivez une fonction en C et une structure avec une fonction membre unique en C++. Générez les codes assembleur

correspondants et recherchez les noms de fonctions qui sont produits par votre fonction C et votre fonction membre C++, de telle manière que vous puissiez voir quelle décoration de nom est mise en oeuvre par le compilateur.

11 Ecrivez un programme avec du code de compilation conditionnelle au sein de main(), de telle manière que lorsqu'une constante pré-processeur est définie, un message est affiché, alors qu'un autre message est affiché lorsqu'elle n'est pas définie. Compilez ce code en expérimentant avec un #definedans le programme, puis recherchez comment vous pouvez passer des définitions pré-processeur via la ligne de commande et expérimentez.

12 Ecrivez un programme qui utilise assert()avec un argument qui est toujours faux (zéro) pour voir ce qui se passe lorsque vous l'exécutez. Maintenant, compilez-le avec #define NDEBUGet exécutez-le à nouveau pour voir la différence.

13 Créez un type abstrait de donnée qui représente une cassette vidéo dans un étalage de location de vidéos.

Essayez de considérer toutes les données et opérations qui peuvent être nécessaire au type Videopour se comporter de manière adéquate au sein du système de gestion de location de vidéos. Incluez une fonction membre print()qui affiche les informations concernant la Video.

14 Créer un objet Stackpour contenir les objets Videode l'exercice 13. Créez plusieurs objets Video, stockez-les dans l'objet Stack, et affichez-les en utilisant Video::print().

15 Ecrivez un programme qui affiche toutes les tailles des types de données fondamentaux sur votre ordinateur en utilisant sizeof.

16 Modifiez Stashde manière à utiliser un vector<char>comme structure de donnée sous-jacente.

17 Créez dynamiquement des emplacements mémoires pour les types suivants, en utilisant new: int, long, un tableau de 100 chars, un tableau de 100 floats. Affichez leurs adresses et puis libérez les espaces alloués à l'aide de delete.

18 Ecrivez une fonction qui prend un char*en argument. En utilisant new, allouez dynamiquement un tableau de chars qui est de la taille du tableau de chars passé à la fonction. En utilisant l'itération sur les indices d'un tableau, copiez les caractères du tableau passé en argument vers celui alloué dynamiquement (n'oubliez pas le marqueur de fin de chaîne null) et retournez le pointeur sur la copie. Dans votre fonction main(), testez la fonction en y passant une constante chaîne de caractères statique entre guillemets, puis récupérez le résultat et passez-le à son tour à la fonction. Affichez les deux chaînes de caractères et les deux pointeurs de

manière à vous rendre compte qu'ils correspondent à des emplacements mémoire différents. A l'aide de delete, nettoyez tout l'espace alloué dynamiquement.

19 Montrez un exemple d'une structure déclarée à l'intérieur d'une autre structure (une structure imbriquée).

Déclarez des membres données dans chacunes des structures, et déclarez et définissez des fonctions membres dans chacunes des structures. Ecrivez une fonction main()qui teste vos nouveaux types.

20 Quelle est la taille d'une structure? Ecrivez un morceau de code qui affiche la taille de différentes structures.

Créez des structures qui comportent seulement des données membres et d'autres qui ont des données membres ainsi que des fonctions membres. Puis, créez une structure qui n'a aucun membre du tout. Affichez toutes les tailles correspondantes. Expliquez le pourquoi du résultat obtenu pour la structure ne contenant aucun membre donnée du tout.

21 C++ crée automatiquement l'équivalent d'un typedefpour les structures, comme vous l'avez appris dans ce

chapitre. Il fait la même chose pour les énumérations et les unions. Ecrivez un petit programme qui démontre cela.

22 Créez un Stackqui contient des Stashes. Chaque Stashcontiendra 5 lignes d'un fichier passé en entrée.

Créez les Stashes en utilisant new. Chargez le contenu d'un fichier dans votre Stack, puis réaffichez-le dans sa forme origninale en extrayant les données de la structure Stack.

23 Modifiez l'exercice 22 de manière à créer une structure qui encapsule le Stackde Stasheses. L'utilisateur doit non seulement être en mesure d'ajouter et d'obtenir des lignes par l'intermédiaire de fonctions membres, mais sous le capot, la structure doit utiliser un Stackde Stashes.

24 Créez une structure qui contient un intet un pointeur sur une autre instance de la même structure. Ecrivez une fonction qui prend l'adresse d'une telle structure et un intindiquant la longueur de la liste que vous désirez créer. Cette fonction créera une chaîne entière de ces structures ( une liste chaînée), en démarrant à la position indiquée par l'argument (la têtede la liste), chaque instance pointant sur la suivante. Créez les nouvelles structures en utilisant new, et placez le compte (de quel numéro d'objet il s'agit) dans le int. Dans la dernière structure de la liste, mettez une valeur de zéro dans le pointeur afin d'indiquer que c'est la fin.

Ecrivez une seconde fonction qui prend en argument la tête de votre liste, et qui se déplace jusqu'à la fin en affichant la valeur du pointeur et la valeur du intpour chaque noeud de la chaîne.

25 Répétez l'exercice 24, mais placez les fonctions à l'intérieur d'une structure au lieu d'avoir des structures

"brutes" et des fonctions séparées.