【问题标题】:C++ STL: Trouble with string iteratorsC++ STL:字符串迭代器的问题
【发布时间】:2011-02-24 22:03:59
【问题描述】:

我正在制作一个简单的命令行 Hangman 游戏。

void Hangman::printStatus()
{
    cout << "Lives remaining: " << livesRemaining << endl;
    cout << getFormattedAnswer() << endl;
}

string Hangman::getFormattedAnswer()
{
    return getFormattedAnswerFrom(correctAnswer.begin(), correctAnswer.end());
}

string Hangman::getFormattedAnswerFrom(string::const_iterator begin, string::const_iterator end)
{
    return begin == end? "" : displayChar(*begin) + getFormattedAnswerFrom(++begin, end);
}

char Hangman::displayChar(const char c)
{
    return c;
}

(最终,我将更改此设置,以便 displayChar() 显示 - 或用户猜到的字符,但为简单起见,我现在只返回所有内容。)

当我从 VS 2010 构建并运行它时,我得到一个弹出框:

调试断言失败!

xstring 行:78

表达式:字符串迭代器不是 可解引用

我做错了什么?

【问题讨论】:

  • 您能否在调试时尝试获取回溯,以确保您发布的代码会导致此行为?
  • 您使用递归实现是否有特殊原因?迭代实现会更简单。
  • @James 是的,你是对的。我迭代地实现了它,现在它可以工作了。
  • 当 begin == end - 1 时看起来像一个不一样的问题

标签: c++ stl iterator


【解决方案1】:

问题在于评估:

displayChar(*begin) + getFormattedAnswerFrom(++begin, end)

在执行此语句时,很明显您的编译器首先递增begin,返回“下一个”begin 用作getFormattedAnswerFrom 的第一个参数,然后然后取消引用begin 用于 displayChar 的参数。

begin 落后于end 时,begin != end 将运行displayChar(*begin) + getFormattedAnswerFrom(++begin, end)。你的编译器增加了begin,所以现在begin == endbegin 的取消引用是无效的。

另请参阅:Order of evaluation in C++ function parameters

【讨论】:

  • C++ 规范是否定义了二元运算符的子表达式的求值顺序,还是像您的断言一般从右到左?
  • @David:它是 UB,至少对于原始类型而言。对于用户类型,它是未指定的,但也可能是未定义的(我不确切知道)。
  • @David,我不认为 C++ 标准指定是 *begin 还是 ++begin 将首先执行。我不打算断言子表达式的从右到左求值,所以我更新了我的答案。
  • 事实上,我曾经在一家使用英特尔 Visual C++编译器的公司工作。英特尔编译器倾向于从左到右计算,而 Visual C++ 编译器倾向于从右到左计算。这导致了一些问题,如果使用icc 编译某些测试用例会成功,但如果使用cl 编译则会失败。
  • 我认为简单的操作顺序是从左到右的?在任何情况下,这都是为什么 ++x 或 x++ 如果用作更大表达式的一部分应避免使用的另一个示例。
【解决方案2】:

我觉得很好。但是,请记住任何堆或堆栈损坏都可能产生此错误。您需要获取堆栈跟踪并查看正确答案内部,并确保它和所讨论的 Hangman 实例都是有效对象。

另外,我只是有点担心您在这里的功能。他们看起来很奇怪。为什么不直接替换为 std::for_each?

编辑@评论:
如果你有 C++0x,你可以这样做

std::for_each(correctAnswer.begin(), correctAnswer.end(), [this](const char& ref) {
    std::cout << this->displayChar(ref);
});

否则,您将不得不做一些看起来像这样的事情:

struct helper {
    Hangman* ptr;
    void operator()(const char& ref) {
        std::cout << ptr->displayChar(ref);
    }
};
helper Helper;
Helper.ptr = this;
std::for_each(correctAnswer.begin(), correctAnswer.end(), Helper);

【讨论】:

  • 已编辑。您不应该对此有任何问题。
  • 有趣。你能解释一下上面例子的语法吗? [this]有什么意义?
  • 当使用 lambda 捕获时,[this] 是一种特殊的分配,其中 this 指针按值捕获,并且生成的 lambda 被视为有效的成员 lambda。 msdn.microsoft.com/en-us/library/… 严格来说,我不必在体内取消引用它,但无论如何都这样做了。
【解决方案3】:

如果correctAnswer 为空,correctAnswer.begin() 将与correctAnswer.end() 相同且不可取消引用。

【讨论】:

  • 那么不应该立即终止递归吗?
  • 他有一张支票。你是说即使 begin == end, displayChar(*begin) + getFormattedAnswerFrom(++begin, end);还会被计算吗?
  • @jpalecek: 是的,没错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-24
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 2011-07-22
  • 2012-10-30
  • 2011-05-01
相关资源
最近更新 更多