考虑这段代码(VS2008):
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);
}
}
如果重要:这是来自用于过滤和修改文档中的单词的家庭作业的代码。 document
保存文档(以前从文件中读取)
我想介绍一个恶意goto,因为我觉得它在这种情况下实际上更清晰:
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);
}
}
目前这是否是一个好的设计选择是无关紧要的。编译器抱怨error C2362: initialization of 'words' is skipped by 'goto SkipAndRestart'
我不明白这个错误。为什么跳过单词初始化很重要,也是错误的?这正是我想要发生的事情,我不希望它做更多工作,只需重新启动血腥循环。继续宏不是或多或少完全相同吗?
答案 0 :(得分:15)
您正在跳过words
数组的构造:
if(line.size() < 2)
goto SkipAndRestart;
std::vector<std::string> words = Utility::split(line);
// ...
SkipAndRestart:
你可以在words
标签之后使用SkipAndRestart:
,这本来就是个问题。你没有在你的情况下使用它,但是words
变量在引入它的作用域的 end 之前不会被破坏,所以就编译器而言,该变量仍然在标签处使用。
您可以通过将words
置于其自己的范围内来避免这种情况:
if(line.size() < 2)
goto SkipAndRestart;
{
std::vector<std::string> words = Utility::split(line);
// ...
}
SkipAndRestart:
请注意,continue
语句跳转到循环的末尾,实际无法放置标签。这是在循环中破坏任何局部变量之后,但在跳回到循环顶部之前的一个点。
答案 1 :(得分:2)
使用goto
,您正在跳过这一行:
std::vector<std::string> words = Utility::split(line);
这不只是跳过重新启动,它正在跳过创作。因为该变量是在循环内定义的,所以每次循环迭代时都会创建它。如果您跳过该创建,则无法使用它。
如果你想创建一次,你应该把那条线移到循环之外。
我会避开我的第一个倾向,即告诉你在同一句话中使用goto
和cleaner
通常是错误的 - 有些情况goto
更好但是我不确定这是否是其中之一。我要告诉你的是,如果这是作业,goto
是一个坏主意 - 任何教育者都不会对goto
的使用感到沮丧。
答案 2 :(得分:1)
总有人认为goto
代码更具可读性,重构使用(inline
)函数至少同样好,没有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++);
}
}
我没有费心去理解内循环的作用,所以我留给你一个有意义的名字。请注意,一旦你给它一个名字,我就不需要了解它的算法,以便知道它的作用,因为这个名字就说明了一切。)
请注意,我已将std::getline()
替换为手写的行提取,这使代码更小。
答案 3 :(得分:0)
另一种解决方案是仅在“ if”指令之前声明“ words”(以防您真的无法将矢量填充到“ if”上方),然后再填充矢量
// HERE!!!!!!
std::vector<std::string> words;
if(line.size() < 2)
goto SkipAndRestart;
words = Utility::split(line);