IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

imbrications des blocs if


Sujet :

C++

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 70
    Points : 50
    Points
    50
    Par défaut imbrications des blocs if
    Bonjour,

    C'est très probablement une bête question, mais à chaque fois que j'écris une fonction, je me demande toujours s'il est mieux d'écrire les conditions de manières imbriquées ou de les mettre l'une à la suite de l'autre en invoquant un return dans le cas d'une erreur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    fn ()
    {
      if (a)
      {
        if (b)
        {
          if (c)
          {
            return true;
          }
        }
      }
     
      return false;
    }
    ou

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    fn ()
    {
      if (!a)
      {
        return false;  
      }
     
      if (!b)
      {
        return false;  
      }
     
      if (!c)
      {
        return false;  
      }
     
      return true;
    }
    Dans un soucis de clareté, je préfère la deuxième version. Mais si j'utilise des variables locales après mes conditions, seront elles déclarées et initialisées lors de l'éxécution, si une condition ne passe pas ?

    Voila merci.

  2. #2
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Tu soulèves là un débat qui va générer des pages de troll . Plus sérieusement, tout dépend du code, du nombre d'imbrications, etc. La deuxième technique est intéressante pour réduire la complexité cyclomatique quand tu as vraiment beaucoup d'imbrications.

    Citation Envoyé par le_ptit_lutin Voir le message
    Dans un soucis de clareté, je préfère la deuxième version. Mais si j'utilise des variables locales après mes conditions, seront elles déclarées et initialisées lors de l'éxécution, si une condition ne passe pas ?
    N'aie pas peur pour tes variables : elles ne seront initialisées que si la fonction ne se termine pas avant. Le comportement que tu crains est celui de Javascript et n'a pas cours en C++.
    Find me on github

  3. #3
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Il n'y a pas à proprement parler de "règle officielle", mais, si l'on ne s'intéresse qu'à la clarté du code, le deuxième que tu présentes est, effectivement plus lisible.

    Cependant, cette manière de travailler n'est pas forcément adéquate dans toutes les circonstances: Si tu as des conditions qui ne doivent être testées que si l'une ou l'autre condition est elle-même vérifiée, par exemple.

    Ce qu'il faut comprendre, c'est que l'imbrication des conditions aura un effet proche du ET logique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    if( a){
        if (b){
            if(c){
                /* correspond à if (a && b && c);    
            }
        }
    }
    alors que les conditions successives auront un effet plus proche du OU logique:... Voir, tout simplement de la vérification séquentielle pure et simple (en fonction de ce qui est fait dans la condition):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if(!a){
        return;
    }
    if(!b){
        return ;
    }
    if (c){
        /* correspond à peu près (s'il n'y a rien entre les différentes conditions) à
         * if (a || b || c)
         */
    }
    alors que le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    if (a){
        /* ... */
    }
    /* ... */
    if (b){
        /* ... */
    }
    /* ... */
    if (c){
        /* ... */
    }
    /* ... */
    aura simplement pour effet de vérifier a, b et c dans un ordre déterminé

    Et il est possible d'envisager toutes les variations qui te viendraient à l'esprit

    Mais si j'utilise des variables locales après mes conditions, seront elles déclarées et initialisées lors de l'éxécution, si une condition ne passe pas ?
    une variable locale n'existe qu'entre sa déclaration et l'accolade fermante de la portée dans laquelle elle est déclarée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int premier; // premier est disponible partout
    if (a){
        //on peut utiliser premier ici
        int sub_a; // sub_a n'existe que is on entre dans la condition
    } //et est détruit ici
    //debut est disponible ici
    //sub_a n'est pas disponible
    int second; // second n'est disponible qu'à partir d'ici
    if(b){
        // on peut utiliser premier et second ici
        // ON NE PEU PAS utiliser sub_a 
        int sub_b; // sub_b est disponible à partir d'ici
    } // sub_b est détruit ici
    // on peut utiliser premier et second
    // ON NE PEUT PAS utiliser sub_a ni sub_b ici
    Mais le compilateur te le diras si tu essayes d'accéder à une variable qui n'existe pas (ou qui n'existe plus)

    [EDIT] Il y a également quelques aspects "pratiques" à prendre en compte, en fonction de ce qui est testé par les conditions a, b et c.

    Le résultat d'un test est toujours un booléen, qui sera toujours évalué très rapidement.

    Mais l'obtention des données qui seront utilisées pour la comparaison et la comparaison elle-même, afin de fournir le booléen attendu, peuvent prendre énormément de temps.

    Imagines une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class MyClass{
        public:
            MyClass(std::string const & first, 
                        std::string const & second,
                        std::string const & third): first_(first), second_(second), third_(third){}
            std::string const & firstString() const{return first_;}
            std::string const & secondString() const{return second_;}
            std::string const & thirdString() const {return third_;}
            std::string complete() const{
                std::string temp(first_);
                temp.append("_")
                      .append(second_)
                      .append("_")
                      .append(third_);
               return temp;
            }
        private:
            std::string first_;
            std::string second_;
            std::string third_;
    };
    Tu pourrais très bien envisager de créer une variable du type de cette classe sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MyClass koala("koala01", "A_nice_man","from_Belgium");
    la comparaison des chaines de caractères renvoyées par les fonctins firstString, secondString et thirdString prendra, en elle-même, énormément de temps (comparé à la comparaison d'un entier) parce que, dans le pire des cas, ce sont tous les caractères de la chaine qui devront être comparés un à un au caractère équivalant dans la chaine de caractères de référence:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if(koala.first() == "koala02"){
     
    }
    nécessitera de comparer le 'k' avec le 'k', le 'o' avec le 'o', le premier 'a' avec le premier 'a', le 'l' avec le 'l', le second 'a' avec le second 'a', le '0' avec le '0' et le '1' avec le '2' avant d'être en mesure de dire que les deux chaines de caractères sont différentes.

    Et il en ira de même pour toutes les comparaisons de chaines de caractères!

    Ainsi, si tu envisage le test
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(koala.complete() == "koala01_A_nice_man_from_Belgium")
    c'est un test qui prendra énormément de temps, parce que, pour l'exécuter, il aura fallu:
    1. générer une nouvelle chaine de caractères sur base des trois chaines connues de la variable
    2. comparer chaque caractères issu de la chaine de caractères ainsi obtenue avec chaque caractère de la chaine de référence
    avant d'obtenir un résultat.

    au final, si tes tests on simplement pour but de te permettre de sortir de la fonction, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    if(koala.firstString() != "koala01"){
        return ;
    }
    if(koala.secondString() != "A_nice_man"){
        return ;
    }
    if(koala.thirdString() != "from_Belgium"){
        return ;
    }
    std::cout<< koala.complete()<<" nice to see you"<<std::endl;
    cela pourra encore aller: chaque chaine de caractères n'est testée qu'une seule et unique fois.

    Mais si tu base ta logique sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /* ... */
    if(koala.firstString() == "koala01"){
        /* ... */
    }else{
        /* ... */
    }
    std::cout<<"glad to see";
    if(koala.firstString() == "koala01" &&
       koala.secondString()=="A_nice_man"){
        /* ... */
    } else{
        /* ... */
    }
    /* ... */
    if(koala.firstString() == "koala01" &&
       koala.secondString()=="A_nice_man" &&
       koala.thirdString() =="from_Belgium"){
       /* ... */
    } else{
       /* ... */
    }
    tu vas inutilement multiplier tes tests : firstString() sera testée trois fois (donc il y aura trois comparaisons de la chaine de caractères en question) et secondString() sera testée deux fois (donc, il y aura deux comparaison de la chaine).

    Au final, ton programme fera ce que tu veux, mais la logique envisagée sera très clairement beaucoup moins efficace que si les tests avaient été imbriqués.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 70
    Points : 50
    Points
    50
    Par défaut
    Merci pour ces éclaircissements.

    En fait l'idée c'est que j'aimerais séparer les tests des variables d'entrées et le code de la fonction. Donc, je comptais sur la méthode 2 pour éliminer tout les tests préparatoires et garder une imbrication logique pour le reste. J'essaie de respecter au mieux la règle, une fonction = un taff.

    Mais bon voila, ça me turlupinait un peu
    Merci

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par le_ptit_lutin Voir le message
    Merci pour ces éclaircissements.

    En fait l'idée c'est que j'aimerais séparer les tests des variables d'entrées et le code de la fonction. Donc, je comptais sur la méthode 2 pour éliminer tout les tests préparatoires et garder une imbrication logique pour le reste.
    Dans ce cas particulier, la deuxième méthode est effectivement "préférable" car plus lisible (enfin, de mon avis personnel).

    Mais uniquement parce qu'il s'agit de "vérifications préparatoires"
    J'essaie de respecter au mieux la règle, une fonction = un taff.
    Et tu as tout à fait raison

    Note que j'ai édité mon message, pour bien te faire comprendre pourquoi il est difficile d'énoncer une règle générale
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Positionnement des blocs
    Par Manio 54 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 10
    Dernier message: 10/08/2005, 18h05
  2. [Info]Coloration des { blocs }
    Par zerovolt dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 23/02/2005, 12h34
  3. Réponses: 13
    Dernier message: 20/01/2005, 12h40
  4. problème pour faire des blocs
    Par tinkye_winkye dans le forum Mise en page CSS
    Réponses: 6
    Dernier message: 04/01/2005, 14h13
  5. [Astuce][Thread]Utilisation des blocs synchronized
    Par Pill_S dans le forum Concurrence et multi-thread
    Réponses: 14
    Dernier message: 21/07/2004, 14h14

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo