2011-01-23 1 views
5

Considérez ce code (VS2008):avant l'initialisation des variables provoque une erreur du compilateur

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    unsigned int currentLineNo = 1; 

    size_t oldEndOfLine = 0; 
    size_t endOfLine = document_.find('\n'); 
    while(endOfLine != std::string::npos) 
    { 
     std::string line = document_.substr(oldEndOfLine, (endOfLine - oldEndOfLine)); 
     if(line.size() < 2) 
     { 
      oldEndOfLine = endOfLine + 1; 
      endOfLine = document_.find('\n', oldEndOfLine); 
      continue; 
     } 

     std::vector<std::string> words = Utility::split(line); 
     for(unsigned int i(0); i < words.size(); ++i) 
     { 
      if(words[i].size() < 2) 
       continue; 
      Utility::trim(words[i], WordManager::delims); 
      Utility::normalize(words[i], WordManager::replace, WordManager::replaceWith); 

      if(ruleOne(words[i]) && ruleTwo(words[i])) 
      { 
       std::set<Word>::iterator sWIter(words_.find(Word(words[i]))); 

       if(sWIter == words_.end()) 
        words_.insert(Word(words[i])).first->addLineNo(currentLineNo); 
       else 
        sWIter->addLineNo(currentLineNo); 
      } 
     } 
     ++currentLineNo; 

     oldEndOfLine = endOfLine + 1; 
     endOfLine = document_.find('\n', oldEndOfLine); 
    } 
} 

S'il est important: ce code est d'une tâche de travail utilisé pour filtrer et modifier des mots dans un document. document détient le document (lu précédemment du fichier)

Je souhaite introduire un goto malveillant parce que je pense qu'il est en fait plus propre dans ce cas comme ceci:

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    unsigned int currentLineNo = 1; 

    size_t oldEndOfLine = 0; 
    size_t endOfLine = document_.find('\n'); 
    while(endOfLine != std::string::npos) 
    { 
     std::string line = document_.substr(oldEndOfLine, (endOfLine - oldEndOfLine)); 
     // HERE!!!!!! 
     if(line.size() < 2) 
      goto SkipAndRestart; 

     std::vector<std::string> words = Utility::split(line); 
     for(unsigned int i(0); i < words.size(); ++i) 
     { 
      if(words[i].size() < 2) 
       continue; 
      Utility::trim(words[i], WordManager::delims); 
      Utility::normalize(words[i], WordManager::replace, WordManager::replaceWith); 

      if(ruleOne(words[i]) && ruleTwo(words[i])) 
      { 
       std::set<Word>::iterator sWIter(words_.find(Word(words[i]))); 

       if(sWIter == words_.end()) 
        words_.insert(Word(words[i])).first->addLineNo(currentLineNo); 
       else 
        sWIter->addLineNo(currentLineNo); 
      } 
     } 

SkipAndRestart: 
     ++currentLineNo; 

     oldEndOfLine = endOfLine + 1; 
     endOfLine = document_.find('\n', oldEndOfLine); 
    } 
} 

Que ce soit ou non un bon choix de conception est pas pertinent en ce moment. Le compilateur se plaint error C2362: initialization of 'words' is skipped by 'goto SkipAndRestart'

Je ne comprends pas cette erreur. Pourquoi est-il important, et une erreur, que l'initialisation des mots soit ignorée? C'est exactement ce que je veux arriver, je ne veux pas qu'il fasse plus de travail, il suffit de redémarrer la boucle sanglante. La macro continue ne fait-elle pas plus ou moins exactement la même chose?

+0

J'aurais pensé que ce serait juste un avertissement, pas une erreur. Que se passe-t-il si vous utilisez juste 'break' au lieu du goto? –

+5

La plupart des gens ne seraient probablement pas d'accord que la version 'goto' est" plus propre "! –

+0

@Oli: Je sais, c'est pourquoi j'ai dit que la conception de la chose n'est pas pertinente; Je ne veux pas commencer une guerre de flamme: P @ Paul: compile. – IAE

Répondre

13

Vous sautez sur la construction du tableau words:

if(line.size() < 2) 
     goto SkipAndRestart; 
    std::vector<std::string> words = Utility::split(line); 
    // ... 
SkipAndRestart: 

Vous pouvez avoir utilisé words après l'étiquette SkipAndRestart:, ce qui aurait été un problème. Vous ne l'utilisez pas dans votre cas, mais la variable words ne sera pas détruite avant la fin de l'étendue dans laquelle elle est introduite, donc en ce qui concerne le compilateur, la variable est toujours utilisée au point de l'étiquette.

Vous pouvez éviter cela en mettant words l'intérieur de son propre champ d'application:

if(line.size() < 2) 
     goto SkipAndRestart; 
    { 
     std::vector<std::string> words = Utility::split(line); 
     // ... 
    } 
SkipAndRestart: 

Notez que l'instruction continue saute à la fin de la boucle, à un point où vous ne pouvez pas réellement mettre une étiquette . C'est un point après la destruction de toutes les variables locales à l'intérieur de la boucle, mais avant le retour en haut de la boucle.

+0

Ma question était plus axée sur la raison pour laquelle c'est un problème réel. – IAE

+1

Je viens juste d'ajouter quelques mots sur le moment de la destruction qui aidera à expliquer cela. –

+0

Merci beaucoup! ^^ – IAE

2

Avec cette goto, vous sautez la ligne:

std::vector<std::string> words = Utility::split(line); 

Ce n'est pas sauter le re-initilisation, il est sauter la création. Comme cette variable est définie à l'intérieur de la boucle, elle est créée à chaque fois que la boucle est répétée. Si vous ignorez cette création, vous ne pouvez pas l'utiliser.

Si vous voulez le créer une fois, vous devez déplacer cette ligne en dehors de la boucle.

Je vais me empêcher de ma première inclination, qui est de vous dire que l'utilisation goto et cleaner dans la même phrase est généralement mal - il y a des situations où goto est mieux, mais je ne suis pas sûr que ce soit l'un d'entre eux. Ce que je vais vous dire est que, si ce sont les devoirs, goto est une mauvaise idée - tout éducateur désapprouvera l'utilisation de goto.

+0

Les devoirs sont déjà rentrés. J'expérimente ici. Mon professeur FAIT des froncements de sourcils et m'accuse de copier un bon code C++. – IAE

1

Comme toujours lorsque les fonctions que quelqu'un pense goto aurait un code plus lisible, refactoring à utiliser (inline) est au moins aussi bon et sans goto:

// Beware, brain-compiled code ahead! 
inline void WordManager::giveThisAMeaningfulName(const std::string& word, std::string__size_type currentLineNo) 
{ 
    Utility::trim(word, WordManager::delims); 
    Utility::normalize(word, WordManager::replace, WordManager::replaceWith); 
    if(ruleOne(word) && ruleTwo(word)) 
    { 
     std::set<Word>::iterator sWIter(words_.find(Word(word))); 
     if(sWIter == words_.end()) 
      words_.insert(Word(word)).first->addLineNo(currentLineNo); 
     else 
      sWIter->addLineNo(currentLineNo); 
    } 
} 

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    std::string__size_type currentLineNo = 1; 

    std::string line; 
    while(std::getline(line)) 
     if(line.size() > 1) 
     { 
      std::vector<std::string> words = Utility::split(line); 
      if(word.size() > 1) 
       for(unsigned int i(0); i < words.size(); ++i) 
        giveThisAMeaningfulName(words[i], currentLineNo++); 
     } 
} 

Je n'ai pas pris la peine d'essayer de comprendre ce que la boucle intérieure fait, donc je vous laisse pour lui donner un nom significatif. Notez que, une fois que vous lui avez donné un nom, je n'ai pas besoin de comprendre son algorithme pour savoir ce qu'il fait, car le nom l'indique tout.)

Notez que j'ai remplacé votre ligne manuscrite -extraction par std::getline(), ce qui rend le code encore plus petit.

+0

C'est intéressant. Personnellement, j'ai trouvé mon code très lisible et facile à comprendre! Ce que vous avez écrit est une option valide, mais sans comprendre la sémantique de la classe, vous avez déréglé certaines parties de la fonction. C'est ma faute si, je vais juste montrer que j'ai besoin d'écrire un code encore plus lisible :) – IAE

+1

@SoulBeaver: Je n'ai pas mis beaucoup d'effort pour comprendre cela et je savais pourquoi j'ai ajouté cet avertissement au sommet. ':)' Quoi qu'il en soit, voici ce que j'ai essayé de faire: 'goto' est une façon difficile à comprendre et non structurée de sauter, alors que plusieurs alternatives structurées et faciles à comprendre existent:' return', 'break' , 'if' ... À chaque fois que vous êtes tenté d'utiliser' goto', cherchez à casser le code en question pour pouvoir les utiliser à la place. En les utilisant, je devais condenser le code au point où SO n'ajoutait plus de barre de défilement verticale. Et le moins de code, le plus facile à comprendre. – sbi

+0

Je suis en programmation en C++ depuis presque vingt ans et j'ai _never_ utilisé 'goto'. Je n'ai pas encore trouvé un cas où cela rendrait mon code plus clair que d'autres options de refactoring. Notamment la capacité de C++ à ne pas payer pour les fonctions (en les insérant), me permettant de prendre des extraits de code presque arbitraires et de leur donner un nom (le nom de la fonction dans laquelle je les mets), ce qui est bien mieux que plus le contrôle de dépendance explicite qu'il gagne (vous devez transmettre explicitement les données requises à ces fonctions, et si c'est trop, cela vous fait penser à ce que vous faites mal) est un outil très précieux. – sbi