De Java (et C) à C++
Historique Syntaxe basée sur le langage C Bjarne Stroustrup 1979 : C with classes 1983 : renommé C++ 1985 : The C++ Programming Language, première édition 1998 : premier standard 2003 : nouveau standard 2011? : prochain standard
Ce que C++ hérite du C Types de base : int, double, float, char Arithmétique de base : +, -, *, /, %, Tests et boucles : if (condition) { insts; } else { insts; } for (int i=0; i<n; i++) { insts;} while (condition) {insts;} do {insts;} while(condition); Pointeurs et tableaux : char v[10]; int *i;
Langage orienté objets Classes, héritage, généricité, polymorphisme,... Pas de garbage collector! Objets références Collections : STL (Standard Template Library)
Exemple simple Fichier Point.h : class Point { private: double x,y; public: Point(); Point(double _x, double _y); Point(const Point & p); // constructeur par recopie ~Point(); // destructeur void set_x(double _x); double get_x() const; }
Exemple simple Fichier Point.cpp : #include Point.h Point::Point(): x(0), y(0) {} Point::Point(double _x, double _y): x(_x), y(_y) {} Point::Point(const Point & p): x(p.x), y(p.y) {} Point::~Point() {} // Destructeur : rien à faire void Point::set_x(double x) { this->x=x; } double Point::get_x() const { return x; }
Eléments de syntaxe Liste d'initialisation d'un constructeur : Point() : x(0),y(0) { } Méthodes constantes : ne modifient pas les attributs de la classe class Point { int x, y; public: int getx() const { return x; } };
Const Spécifier des paramètres/résultats/méthodes constantes => améliore grandement les performances Attention au placement du const! char toto(point& p) const {... } // Méthode constante const char toto(point& p) {... } // Résultat constant char toto(const Point& p) {... } // Paramètre constant Voir plus loin
Fichier Point.cpp : #include Point.h Exemple d utilisation int main(int argc, char * argv []) { Point p1(3,5); // objet alloué sur la pile, appel au //constructeur (3,5) Point p2; // objet alloué sur la pile (0,0) Point p3 = p1; // objet alloué sur la pile (3,5), appel // au copie-constructeur Point * p3 = new Point(p1); // objet alloué sur le tas double x_p2 = p2.get_x(); p3->set_x(4); /*...*/ delete p3; // appel au destructeur /* appel implicite au destructeur pour les objets sur la pile */ }
Gestion de la mémoire 2 nouveaux opérateurs de gestion de la mémoire : new type : allocation d'un objet unique new type[n] : allocation d'un tableau de taille n delete adresse : libération d'un objet unique delete [] adresse : libération d'un tableau Pas de malloc, allocation dynamique Pas de garbage collector => attention aux fuites de mémoire!
int* pi = new int; // Au lieu de : // int *pi = (int *) malloc(sizeof(int)); float* color = new float[3]; delete pi; // Au lieu de : free(pi); delete [] color; Exemple
Destructeur Rôle : libérer la mémoire! (inverse constructeur) Appelé automatiquement à la fin de la portée d un objet (fermeture d'accolade } ) alloué sur la pile Appelé explicitement lors du delete pour un objet sur le tas Par défaut un destructeur vide est implémenté Un seul destructeur par classe
Exemple class Tableau { }; int *_t; public: Tableau(int s = 10) { _t = new int[s]; } ~Tableau() { delete [] _t; } { Tableau T; } // Destructeur ~Tableau() appelé automatiquement // en fin de portée
Référence & (ampersand) = alias sur une variable ou un objet Exemple : int a = 5; int &b = a; // b est une référence sur a b = 6; // Modifie aussi a : a <- 6; Principale utilisation : passage de paramètres void toto(int &i); // i ~ paramètre in-out void toto(point & p) { double x = p.get_x();...}
Passage de paramètres void f(point p); // Copie locale de p => non modifié globalement void f(point* p); // p est un pointeur, la valeur pointée peut être modifiée void f(point& p); // alias sur p : p peut être modifié dans la fonction void f(const Point* p); // Contenu constant : *p =...; interdit void f(point* const p); // Adresse constante : p++; interdit void f(const Point* const p); // Contenu et adresse constants void f(const Point& p); // Référence constante sur p
Question de goût! void f(point* p) {... } // p peut être nul Passage par pointeur ou par référence void g(point& p) {... } // p pointe sur (référence) qqch int main(int argc, char** argv) { } Point p; Point q;... f(&p); g(p); return 0;
Référence : pièges Une référence doit être initialisée int &b; // Interdit Ré-affectation impossible int a = 5; int b = 6; int &ref = a; ref = b; // ref reste une référence sur a, et a <- 6 Initialisation avec un temporaire interdit si référence non constante Point & ref = Point(); // Interdit const Point & ref = Point(); // OK
Entrées/sorties Soit printf, scanf, soit utilisation des flux #include <iostream> Entrée clavier : int i; std::cin >> i; // Lit un entier Sortie à l'écran : int i = 2; std::cout << "i : " << i << std::endl; Flux d'erreur : std::cerr << "erreur!" << std::endl;
Entrées/sorties fichiers Exemple : ifstream f("infile"); // Ouverture en lecture ofstream g("outfile"); // Ouverture en écriture fstream h("filename",ios::in ios::out); //Ouverture en lecture et écriture char buf[81]; f.getline(buf, 80); // Lit une ligne while (f) { g << buf << std::endl; // Envoie dans g f.getline(buf,80); // Lit la ligne suivante }
Espace de nommage Équivalent du paquetage Java Défini à l aide du mot-clé namespace Intérêt : éviter les conflits de noms entre plusieurs modules namespace pile_de_char { void empiler(char); char depiler(); } namespace pile_de_int { void empiler(int); int depiler(); }
Utilisation Exemple : void main(){ pile_de_char::empiler( x ); if (pile_de_char::depiler()!= x ) std::cerr << "impossible" << std::endl; } Mot-clé using : introduit l'identificateur dans la région déclarative courante : using pile_de_char::empiler(char); => empiler('x'); Grand classique : using std; ou using namespace std;
Extension de fonctions Surcharge : sur la liste des paramètres int puissance(int x, int n){ //calcule x n = x.x x } double puissance(int x, double n){ //calcule x n = e n log x } Arguments par défaut : int ajouter(int a, int b=1) { return a+b; } ajouter(2); // Renvoie 3
Redéfinitions de méthodes La méthode d'une classe dérivée masque la méthode de la classe mère : class Mere { public: void f() {... } }; class Fille: public Mere { // Fille hérite de Mere public: void f() {... } }; Utilisation : void main() { Fille Fi; Fi.f(); // Appel à Fille::f() Fi.Mere::f(); // Appel à Mere::f() };
Liaison statique Sélection d'un attribut ou d'une méthode : Déterminé statiquement à la compilation En fonction du type de la variable (type statique) Exemple : class Mere { public: int f() {... } }; class Fille: public Mere { public: int f() {... } }; void main() { Fille Fi; Mere *ptr = &Fi; int i = ptr->f(); // Appel à Mere::f() (liaison statique) };
Liaison dynamique : fonctions virtuelles Pour que la fonction appelée corresponde au type de l'objet pointé (type dynamique) : Mot-clé virtual Exemple : class Mere { public: virtual int f() {... } }; class Fille: public Mere { public: int f() {... } }; void main() { Fille Fi; Mere *ptr = &Fi; int i = ptr->f(); // Appel à Fille::f() (liaison dynamique) };
Fonctions virtuelles pures Définition d'une fonction sans en donner son implémentation (laissée aux classes dérivées) (abstraite) class Mere { public: virtual int f() = 0; }; class Fille: public Mere { public: int f() {... } }; Une classe ayant une méthode virtuelle pure (classe abstraite) ne peut pas être instanciée C'est comme ça qu'on simule des interfaces en C++
Opérateurs Fonctions à la syntaxe particulière : a + b; c++ ; d += e; f < g; h && i; j >> k; Définition : int operator+(const int& a, const int& b); double operator+=(const double& a); bool operator<(const float& f, const float& g); A surcharger dans vos classes si besoin!
Exemples const Point operator+ (const Point& p1, const Point& p2) { Point p(p1); // Constructeur par recopie p.x += p2.x; p.y += p2.y; return p; } std::ostream& operator<<(std::ostream& o, const Point& p) { o << ( << p._x <<, << p._y << ) ; return o; }
Pièges Argument non constant => appliquer l'opérateur peut le modifier OK pour le flux de sortie o Résultat non constant => on pourrait écrire p1+p2 =... OK pour o <<... Si on renvoie une référence => référence sur un temporaire OK pour le flux de sortie o Cas particuliers (opérateur = par exemple)
Bibliothèques de collections STL Structures de données + itérateurs + algorithmes Exemple : #include <vector> #include <numeric> // Pour accumulate vector<int> v; v.push_back(42); v.push_back(-33); v.push_back(7); sort(v.begin(),v.end()); // Algo utilisé par la STL : introsort for (vector<int>::const_iterator it = v.begin(); it!= v.end(); ++it) cout << *it << endl; cout << Moyenne : << accumulate(v.begin(),v.end(),0.0)/v.size() << endl;