• [^] # Re: Pas uniquement string

    Posté par (site web personnel) . En réponse au journal Switch, chaîne constante et c++. Évalué à 1.

    L'exemple flagrant est les pointeurs, notamment pointeurs de fonction. Si tu le garde non-initialisé tu as un énorme trou de sécurité potentiel.

    C'est quoi la valeur par défaut d'un pointeur de fonction qui as du sens ? nullptr, ou une fonction au hasard ? Les deux cas n'ont pas de sens et sont dangereux, bref, avoir un pointeur "default-initialised" n'as pas de sens.

    Ensuite, il n'y a absolument aucun besoin d'avoir un code instable pour aller chercher ces erreurs, n'importe quel compilateur décent te détectera une variable non-initialisée sur demande.

    Justement ;) : les valeurs non-initialisées sont moins dangereuses que les valeurs initialisée par défaut, car souvent les valeurs non initialisée vont te générer une erreur, alors que les valeurs par défaut ne vont pas t'en générer et tu continues sur un bout de code qui n'a plus de sens.

    On parle bien du cas tu as un bug dans ton code et alors que tu pensais gérer tous les cas de figure, il en manque certains et au lieu de mettre une valeur qui a du sens dans ton code, tu finis avec une valeur par défaut qui n'en a pas.

    Gni ? Tu demandes quoi là ? Evidemment que l'auteur de la classe va mettre ce qu'il désire dans le constructeur par défaut, c'est son code ! Tu veux faire quoi à la place ?

    Je ne veux pas de constructeur par défaut ! Exemple, si tu croises le bout de code suivant Color c;, quel est la valeur d'une Color ? Maintenant tu croises Color c = Color::Black(). Cette seconde solution est :

    a) Bien plus facile à lire / comprendre. Il est plus explicite. Il demande aussi moins de surcharge intellectuel au développeur qui n'a pas à retenir la liste des cas par défaut pour toutes les classes.
    b) Il est moins sujet aux changements des comportements par défaut, je détail ce point qui te faisait bondir.

    Imagine une interface de la classe Color comme suit :

    // Color Triple
    class Color
    {
    ....
    public:
     // Default Color (black)
     Color();
     // Init the triple with (a, b, c)
     Color(const float a, const float b, const float c);
    };

    Ici mon seul contrat concernant le constructeur par défaut c'est le petit commentaire qui dit "black". C'est faible, et demain ce constructeur par défaut peut changer très facilement pour construire une autre couleur, sans doute le blanc, car pour les besoins de ce projet on fait plus du multiplicatif sur les couleurs que de l'additif, donc le blanc semble un meilleur choix par défaut. Tu peux aussi imaginer un refactoring dans lequel on change la classe de gestion de couleur, la plupart des opérations ont la même sémantique, sauf ? Les choix arbitraire des constructeurs par défaut.

    Une autre conception pourrait donner :

    // Color Triple
    class Color
    {
    ....
    public:
     // Smart constructors
     static Color Black();
     static Color White();
     // Init the triple with (a, b, c)
     Color(const float a, const float b, const float c);
    };

    Dans ce cas là, pas de constructeur par défaut, et des constructeurs aux noms bien plus explicites et plus plus robustes, ce n'est pas une valeur par défaut arbitraire, c'est Black ou White, c'est écrit en dur dans le nom de la fonction, donc il faudrait vraiment être pervers pour faire un refactoring dans lequel la nouvelle classe Color renvoie du vert dans sa fonction White.

    En résumé, je préfère l'explicite et j'évite les les valeurs par défauts, les constructeurs par défauts, les arguments par défauts sur les fonction. Tout cela parce que de mon expérience, c'est trop facile de se tromper, et cela coûte trop cher. J'évite aussi les variables non initialisées (ou initialisées par défaut) qui servent de valeur de sortie d'une fonction en passage par référence, parce que c'est encore un coup à se planter. Je veux qu'à tout moment dans mon code, si j'ai accès à une variable, elle ai un sens.