/////////////////////////////////
// Comparaison avec le C
/////////////////////////////////

// C++ est _presque_ un sur-ensemble du C et partage sa syntaxe basique pour les
// déclarations de variables, les types primitifs et les fonctions.

// Tout comme en C, le point d'entrée de votre programme est une fonction
// appelée main, avec un integer comme type de retour.
// Cette valeur constitue l'état de fin d'exécution du programme.
// Voir http://en.wikipedia.org/wiki/Exit_status pour plus d'informations.
int main(int argc, char** argv)
{
    // Les arguments de ligne de commande sont passés avec argc et argv de la
    // même manière qu'en C.
    // argc indique le nombre d'arguments,
    // et argv est un tableau de chaînes façon C (char*)
    // représentant les arguments.
    // Le premier argument est le nom par lequel le programme est appelé.
    // argc et argv peuvent être omis si vous ne vous souciez pas des
    // arguments, nous donnant comme signature de fonction int main()

    // Un état de fin d'exécution 0 indique le succès.
    return 0;
}

// Cependant, C++ varie du C selon certains éléments:

// En C++, les caractères littéraux sont des chars
sizeof('c') == sizeof(char) == 1

// En C, les caractères littéraux sont des ints
sizeof('c') == sizeof(int)

// C++ a un prototypage strict
void func(); // fonction qui ne prend aucun argument

// En C
void func(); // fonction qui peut prendre n'importe quel nombre d'arguments

// Utilise nullptr au lieu de NULL en C++
int* ip = nullptr;

// Les en-têtes standards du C sont disponibles en C++,
// mais sont préfixés avec "c" et n'ont pas de suffixe .h
#include <cstdio>

int main()
{
    printf("Bonjour tout le monde!\n");
    return 0;
}

/////////////////////////////////
// Surchage de fonctions
/////////////////////////////////

// C++ gère la surchage de fonctions
// Chaque fonction fournie prend différents paramètres.

void print(char const* maChaine)
{
    printf("Chaîne %s\n", maChaine);
}

void print(int monEntier)
{
    printf("Mon entier est %d", monEntier);
}

int main()
{
    print("Bonjour"); // Utilise void print(const char*)
    print(15); // Utilise void print(int)
}

/////////////////////////////////////////////
// Arguments par défaut de fonctions
/////////////////////////////////////////////

// Vous pouvez fournir des arguments par défaut pour une fonction s'ils ne sont
// pas fournis par l'appelant.

void faitDesChosesAvecDesEntiers(int a = 1, int b = 4)
{
    // Do something with the ints here
}

int main()
{
    faitDesChosesAvecDesEntiers();      // a = 1,  b = 4
    faitDesChosesAvecDesEntiers(20);    // a = 20, b = 4
    faitDesChosesAvecDesEntiers(20, 5); // a = 20, b = 5
}

// Les arguments par défaut doivent être à la fin de la liste des arguments.

void invalidDeclaration(int a = 1, int b) // Erreur !
{
}


//////////////////////////
// Espaces de nom
//////////////////////////

// Les espaces de nom fournissent une séparation des portées pour les
// variables, fonctions, et autres déclarations.
// Les espaces de nom peuvent être imbriqués.

namespace Premier {
    namespace Imbrique {
        void foo()
        {
            printf("Ceci est le Premier::Imbrique::foo\n");
        }
    } // fin de l'espace de nom Imbrique
} // fin de l'espace de nom Premier

namespace Second {
    void foo()
    {
        printf("Ceci est le Second::foo\n")
    }
}

void foo()
{
    printf("Ceci est un foo global\n");
}

int main()
{
    // Inclut tous les symboles de l'espace de nom Second dans la portée
    // actuelle. Notez que le foo() simple ne marche plus, car l'appel est
    // ambigu entre le foo de l'espace de nom Second et celui de premier
    // niveau.
    using namespace Second;

    Second::foo(); // imprime "Ceci est le Second::foo"
    Premier::Imbrique::foo(); // imprime "Ceci est le Premier::Imbrique::foo"
    ::foo(); // imprime "Ceci est un foo global"
}

/////////////////////////
// Entrée/Sortie
/////////////////////////

// Les entrées et sorties en C++ utilisent des flux (streams)
// cin, cout et cerr représentent stdin, stdout et stderr.
// << est l'opérateur d'insertion et >> est l'opérateur d'extraction.

#include <iostream> // Inclusion pour les flux d'entrée/sortie

// Les flux sont dans l'espace de nom std (librairie standard)
using namespace std;

int main()
{
   int monEntier;

   // Affiche sur stdout (ou le terminal/l'écran)
   cout << "Entrez votre chiffre favori:\n";
   // Prend l'entrée clavier
   cin >> monEntier;

   // cout peut également être formaté
   cout << "Votre chiffre favori est " << monEntier << "\n";
   // imprime "Votre chiffre favori est <monEntier>"

    cerr << "Utilisé pour les messages d'erreurs";
}

/////////////////////////////////
// Chaînes de caractères
/////////////////////////////////

// Les chaînes de caractères en C++ sont des objets et ont plusieurs fonctions
// membres
#include <string>

// Les chaînes de caractères sont aussi dans l'espace de
// nom std (librairie standard)
using namespace std;

string maChaine = "Bonjour";
string monAutreChaine = " tout le monde !";

// + est utilisé pour la concaténation.
cout << maChaine + monAutreChaine; // Bonjour tout le monde !"

cout << maChaine + " toi !"; // "Bonjour toi !"

// Les chaînes de caractères C++ sont mutables.
maChaine.append(" le chien !");
cout << maChaine; // "Bonjour le chien !"


//////////////////////
// Références
//////////////////////

// En plus des pointeurs comme ceux en C,
// C++ possède des _références_.
// Ce sont des types de pointeurs qui ne peuvent pas être réassignés
// une fois initialisés, et ne peuvent pas être nulles.
// Ils partagent la même syntaxe que les variables elles-mêmes:
// les * ne sont pas nécessaires pour les déréférencer et
// & (addresse de) n'est pas utilisé pour l'assignement.

using namespace std;

string foo = "Je suis foo";
string bar = "Je suis bar";


string& fooRef = foo; // Ceci créé une référence à foo
fooRef += ". Salut!"; // Modifie foo à travers la référence
cout << fooRef; // Affiche "Je suis foo. Salut!"

// Ne réassigne pas "fooRef". Ceci revient à faire "foo = bar", et
// foo == "I am bar"
// après cette ligne.
cout << &fooRef << endl; // Affiche l'adresse de foo
fooRef = bar;
cout << &fooRef << endl; // Affiche toujours l'adresse de foo
cout << fooRef;  // Affiche "Je suis bar"

// L'adresse de fooRef reste la même, c.-à-d. référence toujours foo.


const string& barRef = bar; // Créé une référence constante de bar.
// Comme en C, les valeurs constantes (et pointeurs et références) ne peuvent
// être modifiées.

// Erreur, les valeurs constantes ne peuvent être modifiées.
barRef += ". Salut!";

// Parenthèse: avant de développer le sujet des références, nous devons
// introduire un concept appelé un objet temporaire. Supposons que nous ayons
// le code suivant :
string objetTemporaireFun() { ... }
string valeurRetenu = objetTemporaireFun();

// Les différents événements se déroulant à la seconde ligne sont :
//   - un objet chaîne de caractères est retourné de objetTemporaireFun
//   - une nouvelle chaîne de caractères est construite avec la valeur
//     retournée comme argument du constructeur
//   - l'objet retourné est détruit.
// L'objet retourné est appelé un objet temporaire. Les objets temporaires sont
// créés chaque fois qu'une fonction retourne un objet, et sont détruits à la
// fin de l'évaluation de l'expression fermante (c'est ce que le standard
// énonce, mais les compilateurs sont autorisés à changer ce comportement.
// Cherchez "optimisation valeur de retour" si vous êtes intéressé par ce genre
// de détails).
// Dans cette ligne de code :
foo(bar(objetTemporaireFun()))

// en supposant que foo et bar existent, l'objet retourné de objetTemporaireFun
// est passé à bar, et est détruit avant que foo soit appelé.

// Revenons maintenant aux références. L'exception à la règle "objet détruit à
// la fin de l'expression fermante" s'applique dans le cas d'un objet
// temporaire lié à une référence constante, où sa durée de vie se voit
// prolongée à la portée courante :

void referenceConstanteObjetTemporaireFun() {
  // referenceConst prend l'objet temporaire, et est valide jusqu'à la fin de
  // la fonction.
  const string& referenceConst = objetTemporaireFun();
  ...
}

// Un autre type de référence introduit en C++11 est spécifiquement pour les
// objets temporaires. Vous ne pouvez pas avoir de variable de ce type, mais
// il prime dans la résolution de surcharge :

void fonctionFun(string& s) { ... }  // Référence régulière
void fonctionFun(string&& s) { ... }  // Référence un objet temporaire

string foo;
// Appelle la version avec référence régulière
fonctionFun(foo);

// Appelle la version avec référence temporaire
fonctionFun(objetTemporaireFun());

// Par exemple, vous aurez ces deux versions de constructeurs pour
// std::basic_string :
basic_string(const basic_string& other);
basic_string(basic_string&& other);

// L'idéal étant de construire une nouvelle chaîne de caractères avec un objet
// temporaire (qui sera détruit de toute façon), nous pouvons ainsi avoir un
// constructeur qui "sauve" des parties de cette chaîne de caractères
// temporaire. Vous verrez ce concept sous le nom de "sémantique de mouvement".

////////////////////////
// Enumérations
////////////////////////

// Les énumérations sont un moyen d'assigner une valeur à une constante
// fréquemment utilisée pour une meilleure visualisation et lecture du code.
enum ETypesDeVoitures
{
  Berline,
  Hayon,
  4x4,
  Break
};

ETypesDeVoitures ObtenirVoiturePreferee()
{
	return ETypesDeVoitures::Hayon;
}

// En C++11, il existe une manière simple d'assigner un type à une énumération,
// ce qui peut-être utile en sérialisation de données et conversion
// d'énumérations entre le type voulu et ses constantes respectives.
enum ETypesDeVoitures : uint8_t
{
  Berline, // 0
  Hayon, // 1
  4x4 = 254, // 254
  Hybride // 255
};

void EcrireOctetDansLeFichier(uint8_t ValeurEntree)
{
	// Sérialise la valeur d'entrée dans un fichier
}

void EcrireTypeVoiturePrefereDansLeFichier(ETypesDeVoitures TypeVoitureEntree)
{
	// L'énumération est implicitement convertie en uint8_t du à la déclaration
    // de son type d'énumération
	EcrireOctetDansLeFichier(TypeVoitureEntree);
}

// D'autre part, vous pourriez ne pas vouloir que des énumérations soient
// accidentellement converties en entiers ou en d'autres énumérations. Il est
// donc possible de créer une classe d'énumération qui ne sera pas
// implicitement convertie.
enum class ETypesDeVoitures : uint8_t
{
  Berline, // 0
  Hayon, // 1
  4x4 = 254, // 254
  Hybride // 255
};

void EcrireOctetDansLeFichier(uint8_t ValeurEntree)
{
	// Sérialise la valeur d'entrée dans un fichier
}

void EcrireTypeVoiturePrefereDansLeFichier(ETypesDeVoitures TypeVoitureEntree)
{
	// Ne compilera pas même si ETypesDeVoitures est un uint8_t car
	// l'énumération est déclarée en tant que "classe d'énumération" !
	EcrireOctetDansLeFichier(TypeVoitureEntree);
}

///////////////////////////////////////////////////
// Classes et programmation orientée objet
///////////////////////////////////////////////////

#include <iostream>

// Déclare une classe.
// Les classes sont habituellement déclarées dans les fichiers d'en-tête (.h ou .hpp).
class Chien {
    // Les variables et fonctions membres sont privées par défaut.
    std::string nom;
    int poids;

// Tous les membres suivants sont publiques jusqu'à ce que "private:" ou
// "protected:" soit trouvé
public:

    // Constructeur par défaut
    Chien();

    // Déclaractions de fonctions membres (implémentations à suivre)
    // Notez que nous utilisons std::string ici au lieu de placer
    // using namespace std;
    // au-dessus.
    // Ne jamais utiliser une instruction "using namespace" dans l'en-tête.
    void initialiserNom(const std::string& nomDuChien);

    void initialiserPoids(int poidsDuChien);

    // Les fonctions qui ne modifient pas l'état de l'objet devraient être
    // marquées en constantes avec const.
    // Ceci vous permet de les appeler avec une référence constante vers l'objet.
    // Notez aussi que les fonctions devant être surchargées dans des classes
    // dérivées doivent être explicitement déclarées avec _virtual_.
    // Les fonctions ne sont pas virtuelles par défault pour des raisons de
    // performances.
    virtual void imprimer() const;

    // Les fonctions peuvent également être définies à l'intérieur du corps de
    // la classe. Ces fonctions sont automatiquement "inline".
    void aboyer() const { std::cout << nom << " fait ouaf !\n"; }

    // En plus des constructeurs, C++ fournit des destructeurs.
    // Ils sont appelés quand l'objet est supprimé ou dépasse le cadre de sa
    // portée. Ceci permet de puissants paradigmes tels que RAII
    // (voir plus loin)
    // Le destructeur devrait être virtuel si la classe est abstraite;
    // s'il n'est pas virtuel, alors le destructeur de la classe dérivée ne
    // sera pas appelé si l'objet est détruit par le biais d'une référence à la
    // classe de base ou d'un pointeur.
    virtual ~Chien();

}; // Un point virgule doit clôre la définition de la classe.

// Les fonctions membres de la classe sont habituellement implémentées dans des
// fichiers .cpp.
Chien::Chien()
{
    std::cout << "Un chien a été construit\n";
}

// Les objets (comme les chaînes de caractères) devraient être passés par
// référence si vous les modifiez ou par référence constante si vous ne les
// modifiez pas.
void Chien::initialiserNom(const std::string& nomDuChien)
{
    nom = nomDuChien;
}

void Chien::initialiserPoids(int poidsDuChien)
{
    poids = poidsDuChien;
}

// Notez que le mot-clé "virtual" est nécessaire uniquement à la déclaration,
// et non à la définition.
void Chien::imprimer() const
{
    std::cout << "Le chien s'appelle " << nom << " et pèse " << poids << "kg\n";
}

Chien::~Chien()
{
    cout << "Au revoir " << nom << " !\n";
}

int main() {
    Chien monChien; // imprime "Un chien a été construit"
    monChien.initialiserNom("Barkley");
    monChien.initialiserPoids(10);
    monChien.imprime(); // imprime "Le chien s'appelle Barkley et pèse 10 kg"
    return 0;
} // prints "Au revoir Barkley !"

// Héritage :

// Cette classe hérite de toutes les propriétés publiques et protégées de la
// classe Chien ainsi que celles privées, mais n'ont pas accès direct aux
// membres et méthodes privés sans l'aide d'une méthode publique ou protégée
class ChienDomestique : public ChienDomestique {

    void definirProprietaire(const std::string& proprietaireDuChien);

    // Surcharge le comportement de la fonction d'impression pour tous les
    // ChienDomestiques.
    // Voir https://fr.wikipedia.org/wiki/Polymorphisme_(informatique)#Polymorphisme_par_sous-typage
    // pour une introduction plus générale si vous n'êtes pas familier avec le
    // concept de polymorphisme par sous-typage (appelé aussi polymorphisme
    // d'inclusion).
    // Le mot-clé "override" est optionnel mais assure que vous surchargez bien
    // la méthode de la classe de base.
    void imprimer() const override;

private:
    std::string proprietaire;
};

// Pendant ce temps, dans le fichier .cpp correspondant :

void ChienDomestique::definirProprietaire(const std::string& proprietaireDuChien)
{
    proprietaire = proprietaireDuChien;
}

void ChienDomestique::imprimer() const
{
    // Appelle la fonction "imprimer" dans la classe de base Chien
    Chien::imprimer();
    std::cout << "Le chien appartient à " << proprietaire << "\n";
    // Affiche "Le chien est <nom> et pèse <poids>"
    //         "Le chien appartient à <proprietaire>"
}

////////////////////////////////////////////////////
// Initialisation et opérateur de surcharge
////////////////////////////////////////////////////

// En C++, vous pouvez surcharger le comportement d'opérateurs tels
// que +, -, *, /, etc.
// La surcharge se fait en définissant une fonction qui sera appelée à chaque
// fois que l'opérateur sera utilisé.

#include <iostream>
using namespace std;

class Point {
public:
    // Les variables membres peuvent avoir des valeurs par défaut
    double x = 0;
    double y = 0;

    // Définit un constructeur par défaut qui ne fait rien
    // mais initialise le Point à la valeur par défaut (0, 0)
    Point() { };

    // La syntaxe suivante s'appelle une liste d'initialisation et est
    // la façon correcte d'initialiser les valeurs des membres d'une classe.
    Point (double a, double b) :
        x(a),
        y(b)
    { /* Ne fait rien à part initialiser les valeurs */ }

    // Surcharge l'opérateur +
    Point operator+(const Point& rhs) const;

    // Surcharge l'opérateur +=
    Point& operator+=(const Point& rhs);

    // Il serait également logique d'ajouter les opérateurs - et -=,
    // mais nous les éclipsons par soucis de concision.
};

Point Point::operator+(const Point& rhs) const
{
    // Créé un nouveau point qui est la somme de celui-ci de rhs.
    return Point(x + rhs.x, y + rhs.y);
}

Point& Point::operator+=(const Point& rhs)
{
    x += rhs.x;
    y += rhs.y;
    return *this;
}

int main () {
    Point haut (0,1);
    Point droite (1,0);
    // Appelle l'opérateur + du Point
    // Le point "haut" appelle la fonction + avec "droite" comme paramètre
    Point resultat = haut + droite;
    // Affiche "Le résultat est haut-droite (1,1)"
    cout << "Le résultat est haut-droite (" << resultat.x << ','
         << resultat.y << ")\n";
    return 0;
}

////////////////////////////////
// Patrons (templates)
////////////////////////////////

// Les templates (patrons) en C++ sont majoritairement
// utilisés pour la programmation générique, bien qu'ils soient bien plus
// puissants que les constructeurs génériques dans d'autres langages.
// Ils gèrent également la spécialisation explicite et partielle ainsi que
// les classes fonctionnelles; en fait, ils sont un langage fonctionnelles
// Turing-complete embedded in C++ !

// Nous commencons avec le genre de programmation générique auquel vous êtes
// peut-être familier. Pour définir une classe ou fonction qui prend un type de
// paramètre particulier :
template<class T>
class Boite {
public:
    // Dans cette classe, T représente n'importe quel type possible.
    void inserer(const T&) { ... }
};

// Pendant la compilation, le compilateur génère des copies de chaque template
// avec les paramètres substitués; ainsi, la définition complète de chaque
// classe doit être présente à chaque appel. C'est pourquoi vous verrez les
// classes de templates définies entièrement dans les fichiers d'en-tête.

// Pour instancier une classe de template sur la pile ("stack") :
Boite<int> boiteDEntiers;

// et vous pouvez l'utiliser comme prévu :
boiteDEntiers.inserer(123);

// Vous pouvez, bien sûr, imbriquer les templates :
Boite<Boite<int> > boiteDeBoites;
boiteDeBoites.inserer(boiteDEntiers);

// Jusqu'à C++11, il était nécessaire de placer un espace entre les deux '>'s,
// sinon '>>' était parsé en tant qu'opérateur de décalage vers la droite.

// Vous croiserez peut-être cette syntaxe
//   template<typename T>
// à la place. Les mot-clé 'class' et 'typename' sont _généralement_
// interchangeables. Pour plus d'explications, allez à
//   http://en.wikipedia.org/wiki/Typename
// ou
// https://fr.wikibooks.org/wiki/Programmation_C-C%2B%2B/Les_templates/Mot-cl%C3%A9_typename
// (oui, ce mot-clé a sa propre page Wikipedia).

// De manière similaire, un patron de fonction :
template<class T>
void aboyerTroisFois(const T& entree)
{
    entree.aboyer();
    entree.aboyer();
    entree.aboyer();
}

// Remarquez ici que rien n'est spécifié à propos du type du paramètre. Le
// compilateur va générer et vérifier le type à chaque appel du patron, c'est
// pourquoi l'appel de fonction suivant marche pour n'importe quel type 'T' qui
// a une méthode constante 'aboyer' !

Chien docile;
docile.initialiserNom("Docile")
aboyerTroisFois(docile); // Affiche "Docile fait ouaf !" trois fois.

// Les paramètres génériques (ou paramètres template) ne sont pas forcément des
// classes :
template<int Y>
void imprimerMessage() {
  cout << "Apprenez le C++ en " << Y << " minutes !" << endl;
}

// Vous pouvez explicitement spécialiser les templates pour un code plus
// optimisé. Bien sûr, les utilisations effectives de la spécialisation ne sont
// pas aussi triviales que celle-ci.
// Notez que vous avez toujours besoin de déclarer la fonction (ou classe)
// comme template, même si vous spécifiez explicitement tous les paramètres.
template<>
void imprimerMessage<10>() {
  cout << "Apprenez le C++ plus vite en seulement 10 minutes !" << endl;
}

// Affiche "Apprenez le C++ en 20 minutes !"
imprimerMessage<20>();
// Affiche "Apprenez le C++ plus vite en seulement 10 minutes !"
imprimerMessage<10>();

//////////////////////////////////
// Gestion des exceptions
//////////////////////////////////

// La bibliothèque standard fournit quelques types d'exception
// (voir http://en.cppreference.com/w/cpp/error/exception)
// mais n'importe quel type peut être lancé en tant qu'exception.
#include <exception>
#include <stdexcept>

// Toutes les exceptions lancées à l'intérieur d'un block _try_ peuvent être
// attrapées par les blocs de traitement d'erreurs (_catch_ handlers).
try {
    // N'allouez pas des exceptions sur le tas (heap) en utilisant _new_.
    throw std::runtime_error("Un problème s'est produit");
}

// Attrapez les exceptions avec des références constantes si ce sont des objets
catch (const std::exception& ex)
{
    std::cout << ex.what();
}

// Attrape n'importe quelle exception non attrapée par les blocs _catch_
// précédents
catch (...)
{
    std::cout << "Exception inconnue attrapée";
    throw; // Re-lance l'exception
}

////////////////
// RAII
////////////////

// RAII signifie "Resource Acquisition Is Initialization", soit l'Acquisition
// d'une Ressource est une Initialisation en français.
// Il est souvent considéré comme le paradigme le plus puissant en C++ et
// est le concept simple qu'un constructeur d'un objet acquiert les ressources
// d'un objet et que le destructeur les libère.

// Afin de comprendre son utilité, considérons une fonction qui utilise la
// gestion d'un fichier C :
void faireQuelqueChoseAvecUnFichier(const char* nomDuFichier)
{
    // Pour commencer, supposons que rien ne peut échouer.

    FILE* fh = fopen(nomDuFichier, "r"); // Ouvre le fichier en lecture

    faireQuelqueChoseAvecLeFichier(fh);
    faireAutreChoseAvec(fh);

    fclose(fh); // Ferme la gestion du fichier.
}

// Malheureusement, les choses deviennent compliquées avec la gestion
// d'erreurs. Supposons que fopen échoue, et que faireQuelqueChoseAvecLeFichier
// et faireAutreChoseAvec retournent des codes d'erreur si elles échouent.
//  (Les exceptions sont le meilleur moyen de gérer l'échec, mais des
//   programmeurs, surtout avec un passif en C,
//   sont en désaccord avec l'utilité des exceptions).
// Nous devons maintenant vérifier chaque appel en cas d'échec et fermer la
// gestion du fichier si un problème se produit.
bool faireQuelqueChoseAvecUnFichier(const char* nomDuFichier)
{
    FILE* fh = fopen(nomDuFichier, "r"); // Ouvre le fichier en mode lecture.
    if (fh == nullptr) // Le pointeur retourné est null à un échec.
        return false; // Signale cet échec à l'appelant.

    // Suppose que chaque fonction retourne faux si elle échoue
    if (!faireQuelqueChoseAvecLeFichier(fh)) {
        fclose(fh); // Ferme le flux d'entrée du fichier pour empêcher les fuites
        return false; // Propage l'erreur
    }
    if (!faireAutreChoseAvec(fh)) {
        fclose(fh);
        return false;
    }

    fclose(fh);
    return true;
}

// Les programmeurs en C clarifient souvent tout cela en utilisant goto :
bool faireQuelqueChoseAvecUnFichier(const char* nomDuFichier)
{
    FILE* fh = fopen(nomDuFichier, "r");
    if (fh == nullptr)
        return false;

    if (!faireQuelqueChoseAvecLeFichier(fh))
        goto echec;

    if (!faireAutreChoseAvec(fh))
        goto echec;

    fclose(fh); // Ferme la gestion du fichier
    return true; // Indique le succès

echec:
    fclose(fh);
    return false; // Propage l'erreur
}

// Si les fonctions indiquent des erreurs en utilisant des exceptions,
// les choses sont un peu plus claires, mais toujours sous-optimales.
void faireQuelqueChoseAvecUnFichier(const char* nomDuFichier)
{
    FILE* fh = fopen(nomDuFichier, "r"); // Ouvre le fichier en lecture
    if (fh == nullptr)
        throw std::runtime_error("Ouverture du fichier impossible.");

    try {
        faireQuelqueChoseAvecLeFichier(fh);
        faireAutreChoseAvec(fh);
    }
    catch (...) {
        // Assurez-vous de bien fermer le fichier si une erreur arrive
        fclose(fh);
        throw; // Puis re-lancer l'exception
    }

    fclose(fh); // Ferme le fichier
    // Tout s'est déroulé correctement
}

// Comparez ceci à l'utilisation de la classe de flux de fichier
// en C++ (fstream).
// fstream utilise son destructeur pour fermer le fichier.
// Pour rappel, les destructeurs sont automatiquement appelée dès qu'un objet
// sort du cadre de sa portée.
void faireQuelqueChoseAvecUnFichier(const std::string& nomDuFichier)
{
    // ifstream is short for input file stream
    std::ifstream fh(nomDuFichier); // Ouvre le fichier

    // Faire des choses avec le fichier
    faireQuelqueChoseAvecLeFichier(fh);
    faireAutreChoseAvec(fh);

} // Le fichier est automatiquement fermé ici par le destructeur

// Ceci a des avantages _énormes_ :
// 1. Peu importe la situation, la ressource (dans ce cas précis la gestion
//    de fichier) sera libérée. Si le destructeur est écrit correctement,
//    il est _impossible_ d'oublier de fermer la gestion et d'entraîner une
//    une fuite de ressources (si l'objet est sur la pile).
// 2. Remarquez que le code est beaucoup plus clair.
//    Le destructeur gère la fermeture du fichier discrètement sans avoir
//    besoin de s'en préoccuper.
// 3. Le code est fiable par rapport aux exceptions.
//    Une exception peut être lancée n'importe où dans la fonction, le
//    nettoyage se fera toujours.

// Tout code C++ idiomatique utilise considérablement RAII pour toutes les
// ressources.
// Des exemples additionnels inclus :
// - La mémoire utilisant unique_ptr et shared_ptr
// - Des conteneurs (containers) - la liste chaînée de la librairie standard,
//   des vecteurs (c.-à-d. tableaux auto-redimensionnés), tables de hachage, et
//   ainsi de suite. Tous détruisent leur contenu quand ils sortent du cadre
//   de leur portée.
// - Les mutex utilisant lock_guard et unique_lock


//////////////////
// Divers
//////////////////

// Ici sont regroupés des aspects du C++ qui peuvent être surprenants aux
// novices (et même à quelques habitués).
// Cette section est, malheureusement, grandement incomplète; C++ est un des
// langages où il est très facile de se tirer soi-même dans le pied.

// Vous pouvez surcharger des méthodes privées !
class Foo {
  virtual void bar();
};
class FooSub : public Foo {
  virtual void bar();  // Surcharge Foo::bar!
};

// 0 == false == NULL (la plupart du temps) !
bool* pt = new bool;
*pt = 0; // Affecte false à la valeur de la variable pointée par 'pt'.
pt = 0;  // Affecte le pointeur null à 'pt'.
// Les deux lignes compilent sans avertissement.

// nullptr est supposé régler un peu ce problème :
int* pt2 = new int;
*pt2 = nullptr; // Ne compile pas
pt2 = nullptr;  // Affecte null à pt2

// Il y a une exception faite pour les booléens.
// Ceci vous permet de tester les pointeurs null avec if(!ptr),
// mais par conséquent, vous pouvez assigner nullptr à un booléen directement !
*pt = nullptr;  // Ceci compile toujours, même si '*pt' est un booléen !

// '=' != '=' != '='!
// Appelle Foo::Foo(const Foo&) ou une variante du (voir sémantiques de mouvement)
// constructeur par copie.
Foo f2;
Foo f1 = f2;

// Appelle Foo::Foo(const Foo&) ou une variante, mais copie seulement la partie
// 'Foo' de 'fooSub'. Tout membre extra de 'fooSub' est ignoré.
// Ce comportement parfois horrifiant est appelé "object slicing".
FooSub fooSub;
Foo f1 = fooSub;

// Appelle Foo::operator=(Foo&) ou une variante.
Foo f1;
f1 = f2;

// Comment vraiment nettoyer un conteneur :
class Foo { ... };
vector<Foo> v;
for (int i = 0; i < 10; ++i)
  v.push_back(Foo());

// La ligne suivante affecte la taille de v à 0, mais les destructeurs ne sont
// appelés et les ressources ne sont pas libérées !
v.empty();
// La nouvelle valeur est copiée dans le premier Foo que nous avons inséré
v.push_back(Foo());

// Ceci nettoie toutes les valeurs de v. Voir la section à propos des objets
// temporaires pour comprendre pourquoi cela fonctionne.
v.swap(vector<Foo>());
