【问题标题】:Sequential Containers || C++ Primer Fifth Edition Exercise 9.22顺序容器 || C++ Primer 第五版练习 9.22
【发布时间】:2014-09-29 10:55:55
【问题描述】:

我对这个练习感到困惑。

练习 9.22:假设 iv 是一个整数向量,下面的程序有什么问题?您将如何纠正这些问题?

vector<int>::iterator iter = iv.begin(),
                      mid  = iv.begin() + iv.size()/2;

while(iter != mid)     
    if(*iter == some_val)
        iv.insert(iter, 2 * some_val)

我要做的第一件事就是去问问这个计划的目的是什么。由于我不确定这本书的这一部分是谁写的,也不想因为一些可能微不足道的事情而打扰作者,所以我开始猜测。

我假设他们想在向量中插入第一个值的双精度值,直到该值位于中间。 (因为他们将 iter 指向开头并且从不通过向量来查找实际值(然后他们再次将其命名为 some_val,查找不在开头的值是一个微不足道的修复,但随后必须验证该值实际上是在容器的前半部分))

所以 a) 向量会像这样增长?

{1,2,3,4,5}
{(2),1,2,3,4,5}
{2,(2),1,2,3,4,5}
{2,2,(2),1,2,3,4,5}

这种情况下的第一个问题是,插入向量很可能会立即使迭代器无效。 我们可以使用一个列表来代替,但是我们不能使用迭代器算术来计算中间值。 像示例中那样只计算一次 mid 是不够的,因为在像 a 这样的列表中。)它会指向 3 并一直指向 3,这只是因为我们调用了迭代器 mid 并用 mid 中的元素初始化了他并不意味着在所有这些插入之后它仍然指向中间。

这就是我所做的:

vector<int>::iterator iter=iv.begin();

while(iter!=iv.begin()+iv.size()/2){    //calculating mid each time
    if(*iter==some_val)
        iv.insert(iter,2*some_val);

    iter=iv.begin();                        //revalidate iter
    while(*iter!=some_val)                  //find the original first element
        ++iter;
}

我看到练习如何迫使您考虑使用哪个顺序容器以及迭代器在不断增长的容器中的行为方式,但是练习的呈现方式仍然让我感到困惑。我是否遗漏了一些明显的观点?

PS:由于整章都在讨论顺序容器,所以我没有关注由 while 循环中的条件引起的问题(就像他们忘记了他们在几章前教过的所有内容)。

【问题讨论】:

  • 我认为练习的重点只是迭代器的失效。你可能想看看std::vector::insert 返回什么。
  • 我注意到的第一件事是iv.instert。不过,它可能不是想要的问题。

标签: c++ iterator containers sequential


【解决方案1】:

让我们一步步的看一遍程序,把代码风格更新为现代C++:

auto iter = iv.begin(); // type will be std::vector<int>::iterator
const auto mid  = iv.begin() + iv.size()/2; // type will be std::vector<int>::iterator

while(iter != mid)
{
  if(*iter == some_val)
    iv.insert(iter, 2 * some_val);
}

前两行创建您的迭代器对。 循环迭代直到到达向量的中间。 循环内的条件检查iter 指向的位置的值是否等于某个预定义值someval。 下一行代码在向量中插入一个新项,等于some_val 值的两倍。

现在回答您的问题:插入行确实使所有迭代器无效,这确实是程序中的问题。此外,迭代器不会迭代,因为它永远不会递增。 一种失效可以通过使用insert 调用的返回值使iter 再次有效和可用来解决:

iter = iv.insert(iter, 2*some_val);

但这仍然给我们留下了mid。您的问题中没有足够的信息来解决这个问题,但可能的选择是:

while(std::distance(iter, iv.end()) > iv.size()) // shift the middle when the vector grows

const auto half = iv.size()/2;
while(std::distance(iter, iv.begin()) < half) // only iterate until iter is halfway the original length by insertion

所以一个示例解决方案可能如下所示:

auto iter = iv.begin();

while(std::distance(iter, iv.begin()) < iv.size()/2) // iterate until halfway the current vector
{
  if(*iter == some_val)
    iter = iv.insert(iter, 2 * some_val);
  else
    ++iter;
}

但这当然可能不是预期的解决方案。

【讨论】:

  • 另外,非常有帮助,谢谢!我很高兴我不是唯一一个认为这个问题缺乏信息的人。
  • @JWD 好吧,这个问题本身可能是故意不清楚的,以激发你大脑的内部运作或在课堂上引发讨论。把它放在下面:“创造性地思考代码”;-)
【解决方案2】:

原始代码不前进,并使所有使用的迭代器无效。

 vector<int>::iterator iter = iv.begin(),
                          mid  = iv.begin() + iv.size()/2;

 while(iter != mid)
         if(*iter == some_val)
                  iv.instert(iter, 2 * some_val);

还有很多问题

vector<int>::iterator iter = iv.begin(),
                      mid  = iv.begin() + iv.size()/2;
int s = iv.size()/2; // assuming we should stop at the original position

while(iter != iv.begin() + s) { // update end condition if that is the intended function
     if(*iter == some_val) {
              iter = iv.instert(iter, 2 * some_val); // gets new valid iter
              ++s; // update position of mid 
              ++iter; // skips the inserted
     }
     ++iter; // advance in vector.
}

【讨论】:

  • ++iter 和 ++s 使 (iter != iv.begin() + s) 成为如果 (*iter == some_val) 则永远不会成立的条件。我仍然喜欢你的回答,因为它使我对练习的可能意图最清楚!
  • @JWD,你说得对,我需要跳过插入然后前进,但这只是对练习意图的一种可能解释。
猜你喜欢
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-20
相关资源
最近更新 更多