Exceptions, patrons de fonctions/classes et auto

Exceptions

Un morceau de code peu avoir un comportement variable selon le contexte.

Exemple

int 4 / b; // erreur si b == 0
FILE * f = fopen("emplacement", 'r'); 

Dans le cas ou le comportement n’est pas souhaité ou exceptionnel on dit qu’une exception est lancée/levée par le code. On peut “rattraper” l’exception avec une structure de contrôle de type try { /* ... */ } catch(/* ... */) { /* ... */ }

Exemple

using namespace std;

int division(int a, int b) {
    if(b == 0)
	    throw 1; // exception lancée
	else
		return a / b;
}

int main(void) {
    int val;
    int ret;
    
    cout << "Entrer un nombre" << endl;
    cin >> val;
    try {
	    ret = division(43/val);
    } catch(int e) { // valeur throw = e
        if(e == 1) { // excpetion attrapée
             cout << "Impossible de diviser par zero" << endl;
             return 1;
        }
    }
    cout << "Le resultat de votre division est " << ret << endl;
    
    return 0;
}

Si une exception n’est récupéré à travers une structure de controle try catch elle est renvoyée à la fonction appelante si elle n’est capturée dans la fonction appelante elle est envoyée à la fonction appelante de la fonction appelante .

Si elle n’est n’est jamais capturée alors le programme s’interrompt brutalement (ducoup ce serait comme un exit(n).

On peut cumuler les blocs catch. Si le code est suceptible de lever plusieurs exceptions. Le bloc qui est choisi est le premier qui “correspond” au type de l’exception lancée.

La correspondance est définie par :

Exemple

struct A { /*...*/ };

struct B: public A { /*...*/ };
try {
    B b;
    throw b; // ligne 6
} catch(long c) {
    /* Pas exécuté car B != long */
} catch(A a) {
    /* Récupére l'objet lancé  en ligne 6 */
} catch(B b) {
    /* Ne sera jamais exécuté */
}

Le bloc catch(...) (c’est bien trois point) récupère toute exception non interceptée par les blocs catch précédents.

Exemple

try {
    throw 1; // int est envoyé
} catch(long a) {
    /* Jamais exécuté */
} catch(...) { // c'est bien trois points  
    /* Toujours exécuté */
}

On peut indiquer qu’une fonction ne renvoie pas d’exception en utilisant le mot clé noexcept.

Exemple

int addition(int a, int b) noexcept {
    return a + b;
}

Patron de fonctions de classe

Définition

Un patron de fonction (ou template) est une définition d’une fonction où un des paramètre est un type.

Exemple

// Exemple que l'on a fait en Algo et struct de données 2
template<class T>
T max(T a, T b) {
    if(a > b)
        return a;
    else
        return b;
}

// Exemple donnée pendant le cours de POO
template<typename T> max(T a, T b) {
    if(a > b)
        return a;
    else
        return b;
}

max<int>   (3, 4);
max<float> (3.1, 6.7);

Lors d’un appel à un patron de fonction le compilateur génère le code de la fonction selon les arguments “types”. Pour cette raison les patron de fonction sont a définir dans les fichiers d’entête (fichier.h) et ne sont pas présent dans les fichiers objet (fichier.o)

On peut faire des patron de méthode/opérateur.

Lors d’un appel d’un patron de fonction les arguments “types” peuvent être omis dans ce cas le compilateur essaye de les trouver par unification.

Exemple

// types identique
max(3, 4); // ok ici équivalent à max<int>(3,4);

// Types différents
// max(3, 4l) // erreur ambiguité 
max<long>(3, 4l); // Ok 

On peut préciser plusieurs paramètres : template<typename A, typename B> A f(B a, int c);

Patron de classe

On peut créer des patrons de classe. Dans ce cas les arguments “types” doivent être explicites.

Exemple

struct B{ /*...*/ };

/* Définition */
template<typename T> struct maClVar {
    /* ... */
    
    T machin(int a);
};

/* Utilisation */
maClVar<int> a;
maClVar<maClVar<B>> d;

template<typename T> T maClVar<T>::machin(int a){ /*...*/}

Les paramètres de patron/fonction peuvent être des valeurs connues à la compilation.

Exemple

template<int a>
int f(int a) {
    return a * b;
}

int main(void) {
    int b;
    std::cin >> b;
    std::cout << f<3>(b); // ok
    //std::cout << f<b>(3); // erreur
}

Le mot clé auto

On peut définir une variable avec auto si le contexte permet d’établir le type de la variable à la compilation.

Exemple

auto a = 3; // variable a de type int
auto b = 3.; // variable b de tybe double
//auto c; // erreur