Copy of https://perso.isima.fr/loic/cpp/tp02.fr.php
tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
Institut d'informatique ISIMA
  • twitter
  • linkedin
  • viadeo

[C++] TP 2

Read in English

Date de première publication : 2013/06/11

Concepts objets

L'objectif de cette partie est de créer une classe Point avec quelques attributs en respectant l'encapsulation et de l'instancier. Nous reprenons et compilons les exemples du cours.

Le code se place dans deux voire trois fichiers :

Le code que l'on va enrichir est le suivant :

// Fichier Point.hpp
// Il manque les gardiens mais je vous laisse les ajouter,
// c'est comme en C et le pragma once c'est nul
class Point {
   // par défaut, tout est privé dans une "class"  
   int x;

 public:
  int getX(); 

};

Un fichier entête comme Point.hpp ne se compile PAS sauf si vous savez ce que vous faites !

Voici maintenant le fichier d'implémentation de la classe Point. Notez la forme particulière du #include avec des guillemets

// Fichier Point.cpp

#include <iostream>  // Inclusion d'un fichier standard
#include "Point.hpp" // Inclusion d'un fichier du répertoire courant

int Point::getX() {
  return x;
}

Voici maintenant le point d'entrée du programme main.cpp du cours :

// Fichier main.cpp

#include <iostream> 
#include "Point.hpp"

int main(int, char**) {
  Point p;

  std::cout << p.getX();
  
  return 0;
}

#include "fic" permet d'inclure un fichier fic à partir du répertoire courant.
#include <fic> permet, elle, d'inclure un fichier à partir d'un répertoire standard (typiquement /usr/[local]/include ou d'un chemin donné par l'option -I (i majuscule)

L'initialisation, c'est comme les antibiotiques, c'est pas automatique ! Et un président bien connu dirait What about f.....g valgrind et son prédécesseur Valgrind bless Our Code

Il existe une autre manière de définir une classe : on peut utiliser le mot-clé struct. Tous les membres (donnée ou méthode) d'une class sont privés par défaut alors qu'ils sont publics pour une struct.

Une instance "bavarde" avec les différents types d'allocation

Compléter le code de la classe Bavarde avec un constructeur et un destructeur qui affichent respectivement "Construction de %" et "Tais-toi %" où % est un paramètre fourni à la construction (avec une valeur par défaut de 0)

#include <iostream>

class Bavarde {
  // 
  // Mettre votre code ici
  //
} bizarre(1);  

Bavarde globale(2);

void fonction(Bavarde b) {
  std::cout << "code de la fonction";
}

int main(int, char **) {
  Bavarde b1(3);
  Bavarde b2(4);
  Bavarde * p = new Bavarde(5);
  // fonction(b1);
  
  return 0;
}

Si on place le code précédent dans des fichiers séparés, il faut se méfier des définitions de bizarre et globale.

Si la notion destructeur est nébuleuse, il faut juste savoir que c'est une méthode spéciale qui est appelée lors de la destruction de l'objet. Sa définition peut ressembler à cela :

~Bavarde() {
  std::cout << "Tais-toi " << n << std::endl;
}  

Dans le script original, Terminator ne cherchait pas Sarah Connor mais plutôt l'instance *p.

Une instance est détruite mais la construction n'est pas signalée. C'est donc qu'il y a une construction implicite quelque part. Cela sera abordé en cours : le C++ passe les variables par valeur (copie) comme en C.

int main(int, char **) {
  std::cout << Bavarde(0).get() << std::endl;
}  

Pampers ....


class Tableau 
{
   int * tab;
   int taille;

 public:
   Tableau():tab(nullptr), taille(10) 
   {
       tab = new int[taille]; // si problème ?
   }

};

int main(int, char **)
{
   Tableau t;

   return 0;
}

Si vous compilez le code donné tel que avec l'option -O2, il n'y aura pas d'erreur car le compilateur s'aperçoit que le code n'est pas vraiment utilisé et donc le code n'est pas généré

Makefile

Voici un exemple de fichier makefile avec génération de dépendance automatique. Le principe est d'exploiter l'option -MMD du compilateur g++. Les fichiers de dépendances (*.d) et objets (*.o) sont créés dans un répertoire superfétatoire build.

Ce makefile se lance toujours par la commande make.

Si vous copiez le code ci-dessous, faites bien attention au fait qu'il faut des tabulations devant les lignes à exécuter

SRC=main.cpp obj.cpp
#SRC=$(wildcard *.cpp)  
EXE=nom_executable

CXXFLAGS+=-Wall -Wextra -MMD -g -O2 -fdiagnostics-color=auto
LDFLAGS= #-lSDL

OBJ=$(addprefix build/,$(SRC:.cpp=.o))
DEP=$(addprefix build/,$(SRC:.cpp=.d))

$(EXE): $(OBJ)
  $(CXX) -o $(EXE) $^ $(LDFLAGS)

build/%.o: %.cpp
  @mkdir -p build
  $(CXX) $(CXXFLAGS) -o $@ -c $<

clean:
  rm -rf build core *.gch $(EXE)

-include $(DEP)

Un @ en début de commande désactive l'affichage sur la sortie standard.

La commande include permet d'inclure d'autres makefiles comme makefile.dep. Si ces fichiers n'existent pas, le préfixe "-" permet de les ignorer.

Ce makefile efface les éventuels fichiers entêtes précompilés (règle clean).

Pour finir, si vous êtes sur une machine avec plus d'un processeur/cœeur et que la commande make prend un certain temps, il est possible d'exploiter la structure de la machine en précisant l'option -j n où n est le nombre de processeurs/cœurs que vous voulez utiliser pour compiler.

Des envies plus importantes ? La documentation est votre amie

Fil rouge ...gesture

Nous allons faire un projet fil rouge, qui nous accompagnera sur un certain nombre de TPs. On pourra mettre en place une structure et des tests unitaires qui évolueront avec les différentes notions vues en cours.

On va s'intéresser à une application de dessin vectoriel en mode texte (sic :-))

Nous allons manipuler des formes telles que des rectangles et des cercles

Créer une classe Rectangle qui a pour propriétés : des coordonnées x et y, une largeur w et une hauteur h. Toutes ces valeurs sont entières. Proposer un constructeur qui permet d'initialiser tous les paramètres. On modélisera plus tard les coordonnées (x,y) comme étant un Point

Rectangle
- x : entier
- y : entier
- w : entier
- h : entier
+ Rectangle(x, y, w, h)
+ toString() : chaine

Créer une classe Cercle qui a les mêmes propriétés que la classe Rectangle (on considérera qu'un cercle est inscrit dans un rectangle). On ajoutera un constructeur qui prend un centre et un rayon.

Cercle
- x : entier
- y : entier
- w : entier
- h : entier
+ Cercle(x, y, w, h)
+ Cercle(cx, cy , rayon)
+ toString() : chaine

La méthode toString() renvoie une chaine de caractères (std::string) qui représente l'objet sous forme texte. Voici deux exemples possibles :


CERCLE 10 10 30 30
RECTANGLE 30 30 15 15

Une base de code est clonable à partir de l'URL : https://gitlab.com/filrouge1/step01.git