- Organisation
- Cours: C++ & Programmation orientée objet
- Introduction
- Exemple de classe
- Méthodes spéciales: Constructeur et Destructeur
- Vie et mort d’un object
- Accès aux membres et méthodes d’un objet
- Droits d’accès:
public
et =private=- NEW Les 3 étapes de la compilation avec
g++
(etgcc
)- NEW Séparation du code
.hpp
et =.cpp=- NEW Qui va où dans mes fichiers
.hpp
,.cpp
etmain.cpp
?- Cours: Révision Git (add, commit, push, etc.)
- Exercices
- Objectifs pour le prochain coaching
Groupe A seulement
Complétez le fichier “Consitution des binômes” avec votre login Windows
et le nom de votre machine Windows sous la forme LOGIN@MACHINE
.
On ne pourra pas avancer tant que cela ne sera pas réglé!
Votre espace de travail dans WSL doit être comme ceci:
Préférez des noms de dossiers/fichiers en minuscules!
# Liste des dossiers INDISPENSABLES: ~/proj7/ ~/proj7/snake/ <- Résultat de la commande =cd ~/proj7/ ; git clone URL snake= ~/proj7/snake/ÉLÈVE1/ ~/proj7/snake/ÉLÈVE2/ # Optionnel: ~/proj7/coach/
Si ce n’est pas le cas, corrigez-le sans délai.
Pour corriger, utiliser les commandes mv OLD_NAME NEW_NAME
.
(Si vous avez des soucis pour renommer un dossier sous le Terminal VS Code, essayer avec le terminal WSL/Debian!)
- Mon dépôt Github.com va passer en mode privé
- Travailler avec un dépôt en mode publique est trop limitant: il va passer en mode privé
- Vous allez être invités à utiliser mon dépôt, pour cela:
Binômes/Trinômes: Dans le fichier “URL dépôts Github” du Drive: complétez les colonnes “Account 1” à “Account 3” avec le nom exact de votre compte Github.com, merci!
- C++ est une évolution du Langage C, il apporte la Programmation Orientée Objet (POO)
Note: On peut mélanger du code C et C++
- En POO, le paradigme change:
- On utilise des classes pour définir des types complexes.
- Analogie: Moule permettant de créer des objets semblables, mais chaque objet aura sa propre vie
- Les classes contiennent:
- Des membres qui sont des variables, elles-même d’un certain type (classe, int, char, etc.)
- Des méthodes qui sont des fonctions dédiées et s’appliquent aux objet de la classe ou à la classe elle-même
- Les classes étant un type, elles permettent de créer des variables, on les appelle objets:
MaClasse mon_object;
- Les Noms Des Classes Sont Prennent En Général Une Majuscule, Tout Comme Un Nom Propre.
Ex:
MaClasse
,Animal
, etc.
MaClasse
contient:
- Un nombre entier
int a;
, il est privé (personne ne peut y accéder à part l’objet) - Une méthode
int add(int b);
qui retournea + b
class MaClasse { public: int add(int b) // méthode publique car définie dans le bloc 'public:' { // return a + b; // Accès implicite à =a=, on préfèrera =this->a= return this->a + b; // "this" est un pointeur sur l'objet lui-même, "->" permet d'accéder à ses membres } private: int a; // membre privé car défini dans le bloc 'private:' }; // Ne pas oublier le ';' final ! MaClasse obj; int c = obj.add(5);
Le parallèle entre C et C++ est relativement simple:
- Type versus Classe:
int x; // variable de type int MaClasse object; // object de type MaClasse
- Fonction versus Méthode:
int add(int a, int b) { return a + b; } // retourne a + b object.add(int b); // retourne object.a + b
- Exemple concrêt: (Vu avec Groupe A seulement)
class Rect { public: void move(int horiz, int vert) { this->c += horiz; this->l += vert; if (this->l < 0) { ...; } }; private: int l, c; // coordonnées ligne, colonne uint8 R, V, B, ALPHA; }; Rect r; r.move(5, 3); // r.V = 255; // Interdit car le membre V de la classe Rect est private!
class MClasse {
public:
MClasse() { // <- Constructeur, ici initialise le membre =a=
this->a = 0;
}
MClasse(int a_) { // Second constructeur
this->a = a_;
}
~MClasse() { // <- Destructeur, ici ne fait rien...
printf("Détruit!");
}
private:
int a;
};
Simple et efficace:
- Le Constructeur est appelé juste après que l’objet ait été créé en mémoire
- Le Destructeur est appelé juste avant que l’objet ne soit détruit en mémoire
- Les objets peuvent être crées comme des variables dynamiques ou non:
void some_code(void) { MaClasse obj1a; // Objet "statique", il mourra en fin du bloc de code {} // Notez comme ce constructeur ne prend pas de "()" // Variante avec arguments dans l'appel au constructeur: int valeur = 5; MaClasse obj1b(valeur); // Fait ceci: "obj1b.a = 5"; MaClasse *obj2; obj2 = NULL; // ... obj2 = new MaClasse(); // Objet dynamique qu'il faudra détruire // ... if (obj2 != NULL) delete obj2; // <- Appelle le Destructeur de l'objet // NE PAS FAIRE: =obj2->~MaClasse();= } // <- Au delà de ce bloc, obj1a et obj1b sont détruits ainsi que obj2 // automatiquement car "statiques", ce n'est pas le cas de *obj2
- Nous venons de voir les deux opérateurs:
new
permettant de créer un objet dynamiquement (donne un pointeur) Symboliquement équivalent à:obj2 = malloc(sizeof(MaClasse))
- puis appelle du Constructeur
MaClasse()
delete
permettant de détruire un objet créé dynamiquement (via un pointeur seulement)- Appelle le Destructeur
~MaClasse()
etfree(obj2)
- Appelle le Destructeur
class MClasse {
public:
int a; // <- Maintenant publique!
int add(int b) { return this->a + b; }
};
// Cas avec objet "statique":
MaClasse obj1;
obj1.a = 1; // <- Accès OK si a est publique dans la classe MaClasse
int c = obj1.add(5);
// Cas avec objet "dynamique":
MaClasse *obj2 = new MaClasse();
obj2->a = 2; // Notation =obj2->a= équivalente à =(*obj2).a=
int d = obj2->add(3);
// Remarques: Un bon réflexe à avoir:
// objDyn->objStatique.a
// objDyn->objDyn->a
Dans une classe C++, on peut définir les droits d’accès aux membres et méthodes:
class MClasse {
public:
int add(int b) { // Cette méthode est publique et utilisable par n'importe quel code
return this->a + b;
}
int GetA() { // "Getter"
return this->a;
}
int SetA(int a_) { // "Setter"
if (a_ < 0 ){
// ERREUR:
return ...;
}
else
this->a = a_;
}
private:
int a; // Ce membre n'est accessible que par l'objet lui-même
};
// Illégal:
MClasse obj1;
int valeur = obj1.a; // ERREUR de compilation!!
MClasse *obj2 = new MClasse();
int valeur = obj2->a; // ERREUR de compilation!!
Note: Il existe également protected
qui s’utilise prend son sens lorsqu’il y a héritage de classes,
nous le verrons plus tard.
Quand on appelle g++ main.cpp snake.cpp -lSDL2 -o snake
, il se passe trois choses:
- Le Preprocessing:
- Modifie (en mémoire) les fichiers
.hpp
et.cpp
avant la compilation (en étape 2) - Utilise les directives du type:
#include
,#define
,#ifndef
,#endif
, etc.Par exemple:
#define SIZE 3
a l’effet d’un Search/ReplaceAll sur les fichiers source dans lesquels on remplace tous les occurrences deSIZE
par3
- Modifie (en mémoire) les fichiers
- Compilation code source:
- Analyse le code source –> Le transforme en code machine
- Génère un fichier =.o= pour chaque fichier =.cpp=
(Les fichiers
.o
sont parfois créés dans une zone temporaire, avecg++
ça se passe dans/tmp/...
)
- L’Édition de liens (linkage effectué sous Linux
ld
):- Rassemble les fichiers
.o
(un par.cpp
) et les assemble en un exécutable (ex:./snake
)
- Rassemble les fichiers
ATTENTION: Si:
- vous écrivez le code des fonctions/méthodes un
fichier.hpp
- vous incluez
#include "fichier.hpp"
dans plusieurs fichiers.cpp
- vous compilez ces fichiers
.cpp
Alors, vous aurez une erreur d’édition de lien car vos fonctions auront été comilées en langage machine
(présentes dans les .o
) autant de fois que vous aurez fait de #include "fichier.hpp"
g++ main.cpp snake.cpp -lSDL2 -o snake
:
- Preprocessing des fichiers :
main.cpp
,snake.cpp
,snake.hpp
,SDL.h
, etc. - Traduction des fichiers
main.cpp
,snake.cpp
–>main.o
,snake.o
- Édition de liens
main.o
,snake.o
,libSDL2
–>./snake
(exécutable)
- On met les déclarations dans les
.hpp
:class
sans le code des méthodes - On protège le contenu de chaque fichier
.hpp
avec#pragma once
:#pragma once # Permet de ne charger le fichier hpp qu'une seule fois // Mon contenu: #include <SDL2/SDL.h> // ... class ... { }
- On met le code dans les
.cpp
, ex:int Maclasse::GetValue() { return this->value; }
- On
#include "..."
tous nos fichiers.hpp
dans nos.cpp
- On compile en passant tous les
.cpp
àg++
=.hpp= = que du déclaratif
#ifndef MACLASSE_HPP
#define MACLASSE_HPP
#include <...>
#include "..." // Mes autres includes du projet si besoin
class MaClasse {
MaClasse();
~MaClasse();
void MyMeth();
int *MyMeth2(int a);
};
#endif
#include <...> // D'abord les include externes/système (=ceux qui ne sont pas à nous)
#include "maclasse.hpp"
MaClasse::MaClasse() {
// ...
}
MaClasse::~MaClasse() {
// ...
}
void MaClasse::MyMeth() {
// ...
}
int *MaClasse::MyMeth2(int a) {
// ...
}
#include <...> // D'abord les include externes/système (=ceux qui ne sont pas à nous)
#include "maclasse.hpp"
int main(void) {
MaClasse obj;
obj.MyMeth();
int *c = obj.MyMeth2(4);
}
Voir fichier howto-git-everyday.org.
Faites les exercices 1 et 2:
Voir les Ressources.
- Terminez votre installation (machine G.Tech1 ou perso)
Au besoin, consultez les divers Howto et la résolution des problèmes
- Terminez les exercices 1 à 3