2011-01-23 10 views
5

Considerate questo codice (VS2008):Goto prima inizializzazione delle variabili provoca errore di compilatore

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); 
    } 
} 

Se è importante: questo è il codice da un compito a casa utilizzato per filtrare e modificare parole in un documento. document tiene il documento (in precedenza leggere dal file)

Vorrei introdurre un goto dannoso perché penso che in realtà è più pulita, in questo caso in questo modo:

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); 
    } 
} 

o meno questa è una scelta buon design è irrilevante in questo momento. Il compilatore si lamenta error C2362: initialization of 'words' is skipped by 'goto SkipAndRestart'

Non capisco questo errore. Perché è importante, e un errore, che l'inizializzazione delle parole venga saltata? Questo è esattamente quello che voglio succedere, non voglio che faccia più lavoro, basta riavviare il ciclo insanguinato. La macro continua non fa più o meno esattamente la stessa cosa?

+0

Avrei pensato che sarebbe stato solo un avvertimento, non un errore. Cosa succede se usi semplicemente 'break' invece del goto? –

+5

La maggior parte delle persone probabilmente non sarebbe d'accordo sul fatto che la versione 'goto' è" più pulita "! –

+0

@Oli: lo so, è per questo che ho detto che il design attuale della cosa è irrilevante; Non voglio iniziare una guerra di fiamme: P @ Paul: Compiles. – IAE

risposta

13

Hai saltando la costruzione della matrice words:

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

Si potrebbe aver usato words dopo la SkipAndRestart: etichetta, che sarebbe stato un problema. Non lo si utilizza nel proprio caso, ma la variabile words non verrà distrutta fino a quando lo termina con lo dell'ambito in cui è stato introdotto, quindi per quanto riguarda il compilatore, la variabile è ancora in uso presso il punto dell'etichetta.

È possibile evitare questo mettendo words all'interno del suo proprio ambito:

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

Nota che la dichiarazione continue salta alla fine del ciclo, in un punto in cui è effettivamente non può mettere un'etichetta . Questo è un punto dopo la distruzione di qualsiasi variabile locale all'interno del ciclo, ma prima del salto indietro fino alla cima del ciclo.

+0

La mia domanda era più orientata sul motivo per cui questo è un problema reale. – IAE

+1

Ho appena aggiunto un po 'del tempo di distruzione che aiuterà a spiegarlo. –

+0

Grazie mille! ^^ – IAE

2

Con questo goto, si sta saltando la linea:

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

questo non è solo per saltare la ri-initilisation, è saltare la creazione. Poiché quella variabile è definita all'interno del ciclo, viene creata di recente ogni volta che il ciclo itera. Se salti quella creazione, non puoi usarla.

Se si desidera che venga creato una volta, è necessario spostare quella linea all'esterno del ciclo.

mi astengo dalla mia prima inclinazione, che è quello di dire che l'utilizzo di goto e cleaner nella stessa frase è di solito sbagliato - ci sono situazioni in cui goto è meglio ma non sono sicuro che questo è uno di loro. Quello che ti dirò è che, se si tratta di compiti a casa, goto è una cattiva idea - qualsiasi educatore disapprova l'uso di goto.

+0

I compiti sono già stati consegnati. Sto solo sperimentando qui. Il mio professore aggrotta le sopracciglia e mi accusa di mettere un buon codice C++. – IAE

1

Come sempre quando qualcuno pensa goto sarebbe codice più leggibile, refactoring di utilizzare (inline) funzioni è almeno altrettanto buono e senza 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++); 
     } 
} 

non ho preso la briga cercando di capire che cosa quel ciclo interno fa, quindi lo lascio a voi per dargli un nome significativo. Nota che, una volta che gli hai dato un nome, non ho bisogno di capire il suo algoritmo per sapere cosa fa, perché il nome dice tutto.)

Nota che ho sostituito la tua linea scritta a mano -estrazione di std::getline(), che ha reso il codice ancora più piccolo.

+0

Questo è interessante. Personalmente ho pensato che il mio codice fosse molto leggibile e facile da capire! Quello che hai scritto è un'opzione valida, ma senza capire la semantica della classe, hai incasinato alcune parti della funzione. È colpa mia, però, solo per dimostrare che ho bisogno di scrivere un codice ancor più leggibile :) – IAE

+1

@SoulBeaver: Non ho fatto molti sforzi per capirlo e sapevo perché ho aggiunto questo disclaimer in cima. ':)' Comunque, quello che ho cercato di trasmettere è questo: 'goto' è un modo difficile da comprendere e non strutturato per saltare, mentre esistono alternative strutturate facili da comprendere:' return', 'break' , 'if' ... Ogni volta che sei tentato di usare' goto', cerca di scomporre il codice in questione per poterli usare. Usandoli, dovevo condensare il codice fino al punto in cui SO non aggiungeva più una barra di scorrimento verticale. E meno codice, più facile è capire. – sbi

+0

Sto programmando in C++ da quasi vent'anni e non ho mai usato 'goto'. Devo ancora trovare un caso in cui renderebbe il mio codice più chiaro di altre opzioni di refactoring. Vale a dire C++ 'capacità di non pagare per le funzioni (mediante inlining), permettendomi di prendere frammenti di codice quasi arbitrari e di schiaffi un nome su di essi (il nome della funzione in cui li sto inserendo), che è molto meglio del commentare, più il controllo esplicito delle dipendenze guadagna (devi passare esplicitamente i dati richiesti a quelle funzioni, e se è troppi, ti fa pensare a quello che stai facendo male) è uno strumento molto prezioso. – sbi

Problemi correlati