Editer C++


Des pense-bêtes.
La plupart des exemples ont été écrits en suivant le tutorial sur Open Classroom, merci à Mathieu Nebra et Matthieu Schaller !



Compiler du C/C++ sur windows



Dans un sous-dossier "build", avec git bash pour pas avoir d'erreur d'antislashes, on spécifie l'emplacement de mingw64 (ici à la racine d'un disque "G:") :
cmake -DCMAKE_CXX_COMPILER=/g/mingw64/bin/g++.exe -DCMAKE_C_COMPILER=/g/mingw64/bin/gcc.exe .. -G "MinGW Makefiles"

Puis : /g/mingw64/bin/mingw32-make.exe"

Pour utiliser nmake, le compilateur microsoft, installer https://download.microsoft.com/download/E/E/D/EEDF18A8-4AED-4CE0-BEBE-70A83094FC5A/BuildTools_Full.exe et chercher nmake dans C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30037\bin\HostX64\x86. Le mettre en variable d'environnement.

La base


Installer Code::Blocks comme IDE.

Nouveau, projet, console, C++.

Récupérer les entrées de l'utilisateur



//utilise la bibliothèque iostream, les commandes Input/output stream, la base pour utiliser le C et traiter des entrées utilisateur et faire des sorties écran. #include <iostream> //biblio pour utiliser des lignes de texte #include <string> //indique qu'on utilise les commandes dans l'espace de nom std (dans iostream) using namespace std; //boucle principale int main() { //declare une var texte vide (une var string est en réalité un objet, tableau de var "char") string nomUtilisateur(""); //ici, un exemple d'alias ("reference") de la var nomUtilisateur string& a(nomUtilisateur); //déclare des variales int (nombre entier) int ageUtilisateur(0); int pauseUtilisateur(0); int calcul1(0); int calcul2(0); //affiche un message en sortie cout << "Quel est votre age ?"<< endl;; //cin pour récupérer un chiffre. Attention, si on entre pas un chiffre, le programme se comportera bizarrement. cin >> ageUtilisateur; //Il faut mettre ça si on utilise getline après un cin, pour éviter que les entrées suivantes soient sauvés en mémoire. //Ca signifie un peu "arrête d'écouter les entrées". cin.ignore(); cout << "Quel est votre nom ?"<< endl; //pour récupérer un texte, utiliser getline getline(cin, nomUtilisateur); //affichage des résultats cout << "Vous avez " << ageUtilisateur << " ans, vous vous appelez"<< nomUtilisateur<< endl;cin.ignore(); cout << "Premier chiffre pour le calcul ?"<< endl; cin >> calcul1; cout << "2e chiffre pour le calcul ?"<< endl;; cin >> calcul2; cout << calcul2 << " et " << calcul1 << " font " << calcul2+calcul1; //dispensable, pour quitter proprement return 0; }


Tests conditionnels


Exemples de if, while, else if, for.

#include <iostream> using namespace std; //POUR TESTER PLUSIEURS CHOSES A LA FOIS : //if (adulte && nbEnfants >= 1) //if (nbEnfants == 1 || nbEnfants == 2) //on pourrait utiliser do while afin d'éviter de devoir mettre la var nbEnfants en négatif pour entrer dans la boucle //do {...} while (nbEnfants < 0); //for (initialisation ; conditions qui font que la boucle se répête ; incrémentation) int main() { int nbEnfants(-1); while (nbEnfants <0){ cout << "Combien d'enfants avez vous ? Merci de ne pas entrer de chiffre négatif !"; cin >> nbEnfants; cin.ignore(); if (nbEnfants == 1){ cout << "Vous avez un enfant, bravo !" << endl; } else if (nbEnfants > 1){ cout << "Vous avez des enfants, bravo !" << endl; } else{ cout << "Vous n'avez pas d'enfant, ou alors vous avez entré un chiffre négatif." << endl; } } for (int compteur = 0 ; compteur < nbEnfants ; compteur++){ cout << compteur << endl; } cout << nbEnfants << " enfants !" << endl << "Fin du programme" << endl; return 0; }


Exemple d'utilisation d'une fonction




#include <iostream> using namespace std; double multiplieSept(double nombreAMultiplier) { nombreAMultiplier*=7; return nombreAMultiplier; } int main() { double nombreSaisi; cout << "Entrez un nombre pour le multiplier par 7." << endl; cin >> nombreSaisi; nombreSaisi=multiplieSept(nombreSaisi); cin.ignore(); cout << nombreSaisi << endl << endl; cout << "Pour rappel, voici la table de 7 :" << endl; for (int i=1 ; i<11 ; i++) { cout << "7 x " << i << "=" << multiplieSept(i) << endl; } return 0; }


Notez que la fonction est avant le main. Il est possible de mettre la fonction après, à condition de l'avoir signalée. Dans l'exemple ci-dessous, la ligne double multiplieSept(double nombreAMultiplier); signale qu'on va utiliser cette fonction. C'est ce qu'on appelle le "prototype" d'une fonction.

#include <iostream> using namespace std; double multiplieSept(double nombreAMultiplier); int main() { double nombreSaisi; cout << "Entrez un nombre pour le multiplier par 7." << endl; cin >> nombreSaisi; nombreSaisi=multiplieSept(nombreSaisi); cin.ignore(); cout << nombreSaisi << endl << endl; cout << "Pour rappel, voici la table de 7 :" << endl; for (int i=1 ; i<11 ; i++) { cout << "7 x " << i << "=" << multiplieSept(i) << endl; } return 0; } double multiplieSept(double nombreAMultiplier) { nombreAMultiplier*=7; return nombreAMultiplier; }


Notez qu'on peut se servir du prototype pour donner une valeur par défaut : double multiplieSept(double nombreAMultiplier=10) (mais dans cet exemple ça n'a pas de sens).

Tableau à taille fixe (avec envoie à une fonction)



#include <iostream> using namespace std; double moyenne (double notes[],int nombreNotes) { double moyenne(0); for(int i(0); i<nombreNotes; ++i) { moyenne += notes[i]; //On additionne toutes les notes } moyenne /= nombreNotes; return moyenne; } int main() { int const nombreNotes(6); double notes[nombreNotes]; notes[0] = 12.5; notes[1] = 19.5; //Bieeeen ! notes[2] = 6.; //Pas bien ! notes[3] = 12; notes[4] = 14.5; notes[5] = 15; cout << moyenne(notes,nombreNotes) << endl; return 0; }


Tableau dynamique



#include <iostream> #include <vector> using namespace std; int main() { int moyenne(0); vector<int> notes; notes.push_back(12.5); //On ajoute des cases avec les notes notes.push_back(19.5); notes.push_back(6); notes.push_back(12); notes.push_back(14.5); notes.push_back(15); for(int i=0;i<5;i++) { moyenne+=notes[i]; } cout << "Moyenne des "<< notes.size() << " notes :" << moyenne/5 << endl; return 0; } //pour envoyer à une fonction : //void fonction(vector<int> a)


Lire, écrire, modifier un fichier



#include <iostream> #include <fstream> #include <string> using namespace std; int main() { string const nomFichier("test.txt"); //output file stream //on dit "output" car on dirige *vers* le fichier (fichier << truc à diriger) //un peu comme quand on dirige vers l'écran (cout << truc à ecrire à l'écran) ofstream monFlux(nomFichier.c_str()); //pour append, faire //ofstream monFlux("test.txt", ios::app); //Pour savoir la position du curseur : monFlux.tellp //pour écrire à la position X : monFlux.seekp(nombreCaracteres, position); if(monFlux) { monFlux << "test écriture dans un fichier" << endl; monFlux << 15.236 << endl; int age(18); monFlux << "J'ai " << age << " ans." << endl; } else { cout << "ERREUR: Impossible d'ouvrir le fichier." << endl; } monFlux.close(); //LECTURE //pour lire un nombre à virgule depuis le fichier //double nombre;monFlux >> nombre; //Pour lire un mot depuis le fichier //string mot;monFlux >> mot; //lettre par lettre //char a;monFlux.get(a); ifstream monFluxLecture("test.txt"); //Ouverture d'un fichier en lecture string ligne; if(monFluxLecture) { while(getline(monFluxLecture, ligne)) { cout << "Position du curseur :" << monFluxLecture.tellg() << "Contenu : " << ligne << endl; } } else { cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << endl; } //on ferme proprement monFluxLecture.close(); monFluxLecture.clear(); //exemple de lecture à la position X //on réouvre le flux monFluxLecture.open("test.txt"); int positionSouhaitee; cout << "A quelle position voulez vous lire ?"; cin >> positionSouhaitee; cin.ignore(); //On s'y déplace depuis le début du fichier //("ios::beg" pour le début, "ios::end" pour la fin, "ios::cur" pour se déplacer depuis la pos actuelle) monFluxLecture.seekg(positionSouhaitee, ios::beg); //on met ce qu'on voit dans la variable ligne. if(getline(monFluxLecture, ligne)){ cout << "Position du curseur :" << monFluxLecture.tellg() << "Contenu : " << ligne << endl; }else{ cout << "Ne peut pas lire."; } return 0; }


TP jeu, trouver le mot aléatoire




#include <iostream> #include <string> #include <ctime> #include <cstdlib> using namespace std; string melangerMot(string motATrouver){ string motMelange; srand(time(0)); int pif(0); //tant que le mot a trouver a encore une lettre while(motATrouver.size()>0){ //on choisit un nombre au pif entre 0 et le nombre total de lettre pif = rand() % motATrouver.size(); //on ajoute la lettre correspondant au n° de la lettre de motATrouver à motMelange motMelange+=motATrouver[pif]; //on supprime la lettre correspondant au n° de la lettre motATrouver.erase(pif, 1); cout << motATrouver; } return motMelange; } int main() { string motATrouver; string motPourDeviner; cout << "Saisissez un mot :"<< endl; getline(cin, motATrouver); do{ cout << endl << "Remettez les lettres '"<< melangerMot(motATrouver) << "' dans le bon ordre :" << endl; getline(cin, motPourDeviner); }while(motPourDeviner!=motATrouver); cout << "Bravo !" << endl; return 0; }


Pointeur


Les variables sont stockés à des endroits précis dans la mémoire de l'ordinateur, par exemple "0x28ff0c".
Pour trouver où une variable est stockée, on la précède de "&" : &ageUtilisateur.
Pour stocker cette information, on utilise un type de variable spéciale : un pointeur. Ce type de variable sera précédé de "*".


#include <iostream> using namespace std; int main() { int ageUtilisateur(18); //mettre une étoile pour indiquer qu'on veut pointer vers une adresse mémoire int *ptr(&ageUtilisateur); cout << "L'adresse de 'ageUtilisateur' est : " << &ageUtilisateur << endl; cout << "La valeur de pointeur est : " << ptr << endl; cout << "La valeur pointée est : " << *ptr << endl; return 0; }


Dans l'exemple ci-dessus on a besoin du nom de la variable pour trouver où elle est stockée. On peut aussi directement pointer vers une adresse mémoire avec pointeur = reinterpret_cast<int*>(0x590d); mais si on pointe n'importe où ça fait surtout crasher le programme.

Dans cet exemple on pointe directement vers l'adresse 0x28ff1c, utilisé par "on ne sait quoi", ça peut fonctionner et donner la valeur de l'adresse mémoire, ou bien ça peut planter :

#include <iostream> using namespace std; int main() { int *pointeur(reinterpret_cast<int*>(0x28ff1c)); cout << "La valeur de pointeur est : " << pointeur << endl; cout << "La valeur pointée est : " << *pointeur << endl; return 0; }


Plusieurs fichiers


En général on découpe un projet en plusieurs fichiers pour éviter qu'on ait toutes les fonctions dans un même fichier et les avoir tout le temps sous le nez.

Par exemple, fichier main.cpp

#include <iostream> //appelle la librairie math.h qui permet de déclarer la fonction ajouteDeux #include "math.h" using namespace std; int main(){ int a(2); cout << ajouteDeux(a) << a << endl; return 0; }


La librairie qui va définir les fonctions (on voit ici les prototypes des fonctions, c'est à dire qu'on annonce les futures fonction) :

#ifndef MATH_H_INCLUDED #define MATH_H_INCLUDED int ajouteDeux(int nombreRecu); #endif // MATH_H_INCLUDED


On pourrait faire "int ajouteDeux(int nombreRecu=10)" si on voulait que le nombre reçu soit 2 par défaut.

Enfin, le fichier avec la fonction "ajoutedeux" :

#include "math.h" int ajouteDeux(int nombreRecu){ int valeur(nombreRecu + 2); return valeur; }


Dans les .h, ne pas utiliser "namespace std;" dans les .h pour éviter l'erreur "string does not name a type". Quand on utilisera une fonction, il faudra la préfixer de std, par exemple : std::cout.

Attention, quand on ajoute un nouveau fichier, il faut bien cocher "Add file" "in buld target" au moment de la création pour éviter une erreur "undefined reference to" (qui signifie que notre fichier n'est pas lié à la publication du programme).

Objets


Explication


Un objet est un morceau de code qui est généré à partir d'une classe (la classe est le plan de cet objet). Les objets ont des attributs (des variables, qui ne sortent en général pas de l'objet) et des méthodes (des fonctions, pour manipuler l'objet ou obtenir des infos sur l'objet). Par exemple, l'objet "string" a la méthode "substr", qu'on appelle ainsi :

int main() { string texte("Texte à couper"); cout << texte.substr(3) << endl; return 0; }

On ne peut pas modifier les attributs de l'objet string, à moins de modifier sa classe.

Exemple


Exemple avec la création d'une classe "oiseau" dont on va se servir pour générer 2 oiseaux :

#include <iostream> #include <string> using namespace std; class Oiseau { //n'importe qui peut accéder à ces methodes et attributs public: //Cette fonction est appelée "constructeur". En initialisant l'objet, que se passe-t-il ? Oiseau(){ //il a une taille par défaut taille=15; } //Sauf si on a saisi une taille Oiseau(int tailleSaisie){ taille=tailleSaisie; } //Methode pour faire chanter l'oiseau string chante() { return "Piou ! Piou !"; } //Methode pour connaitre la taille de l'oiseau int mesure() { return taille; } //seul la classe peut modifier ces méthodes et attributs private: int taille; }; int main() { //un oiseau par défaut et un oiseau avec une taille de 20 Oiseau oiseauGenerique, hirondelle(20); //faire chanter l'oiseau cout << oiseauGenerique.chante() << endl; cout << hirondelle.chante() << endl; //trouver la taille de l'oiseau cout << "L'oiseau generique mesure " << oiseauGenerique.mesure() << endl; cout << "L'hirondelle mesure " << hirondelle.mesure() << endl; return 0; }


Constructeurs


Pour la fonction d'initialisation (le constructeur), il existe également cette écriture alternative, qu'il est très fortement conseillé d'utiliser :

Oiseau() : taille(15), autrenomvar(valeur) { //Rien besoin de mettre ici }


A noter qu'on pourrait donner à "oiseauGenerique" de la façon suivante tous les paramètres d'un autre oiseau qui aurait été créé précédemment (on utilise ici un constructeur créé par défaut, le "constructeur de copie") :
Oiseau oiseauGenerique(autreoiseau);


Constantes


La methode "mesure", pour connaitre la taille de l'oiseau ne modifie pas l'objet. Dans le but de rendre le code plus clair et d'utiliser moins de mémoire, on peut préfixer cette méthode avec le mot-clef "const".
int Oiseau::mesure() const

Destruction d'un objet


La méthode pour détruire un objet n'est pas obligatoire. C'est le nom de l'objet précédé d'un tilde (~) :

Oiseau() { //chose qu'on veut faire à la suppression de l'objet }


Objet dans plusieurs fichiers



Le fichier Oiseau.h va définir la classe et ses propriétés et ses méthodes ;

#include "Oiseau.h" using namespace std; //En initialisant l'objet, que se passe-t-il ? Taille de 15 par défaut Oiseau::Oiseau() : taille(15) { } //Sauf si on a saisi une taille, c'est elle qui sera prise en compte Oiseau::Oiseau(int tailleSaisie):taille(tailleSaisie) { } //Methode pour faire chanter l'oiseau string Oiseau::chante() { return "Piou ! Piou !"; } //Methode pour connaitre la taille de l'oiseau int Oiseau::mesure() const { return taille; }


Le fichier Oiseau.cpp va donner le code de ces méthodes :

#include "Oiseau.h" using namespace std; //En initialisant l'objet, que se passe-t-il ? Oiseau::Oiseau(){ //il a une taille par défaut taille=15; } //Sauf si on a saisi une taille Oiseau::Oiseau(int tailleSaisie){ taille=tailleSaisie; } //Methode pour faire chanter l'oiseau string Oiseau::chante() { return "Piou ! Piou !"; } //Methode pour connaitre la taille de l'oiseau int Oiseau::mesure() { return taille; }


Enfin, le fichier main va lancer tout ça :

#include <iostream> #include "Oiseau.h" using namespace std; int main() { //un oiseau par défaut et un oiseau avec une taille de 20 Oiseau oiseauGenerique, hirondelle(20); //faire chanter l'oiseau cout << oiseauGenerique.chante() << endl; cout << hirondelle.chante() << endl; //trouver la taille de l'oiseau cout << "L'oiseau generique mesure " << oiseauGenerique.mesure() << endl; cout << "L'hirondelle mesure " << hirondelle.mesure() << endl; return 0; }