迭代器的最佳编码风格

时间:2012-01-10 17:05:17

标签: c++ coding-style

一位天花飞过的人说,这让他的眼睛流血了:

for (std::vector<Agent*>::const_iterator iter = agents.begin(); iter != agents.end(); ++iter)
{
    delete *iter;
}

什么是更简洁的写作方式?我想我可以输入解析迭代器类型,但是在某些类中,这意味着我认为顶部的一大块typedef看起来很糟糕。

10 个答案:

答案 0 :(得分:3)

你已经得到了很多答案,但我会认为这个问题比他们所暗示的要深得多。他们中的大多数人主要(或专门)使用您正在使用的语法,我认为这通常是问题中最少的问题。他们正在治疗症状,而不是疾病。

对于您发布的for循环有意义,您有一组指向分配有new的对象的指针。此外,您要删除容器中的所有对象这一事实意味着您基本上将对象的所有权与该容器相关联。

正如我上面所说,for循环是一种症状。这种疾病就是设计。修复设计时,您不必担心for循环的语法,因为您根本不需要该循环。

这留下了一个严肃的问题:为什么你首先存储指向对象的指针?也许这些对象的复制成本很高,而且你担心当向量扩展它的分配时,复制对象会太慢。有可能在这种情况下,你只是错了。众所周知,vector以指数方式扩展其分配,因此插入已经分摊了不变的复杂性。不太为人所知或显而易见的是,相同的指数增长也意味着现有对象的平均拷贝数渐近逼近常数。在典型的实现中,平均拷贝数将介于2和3之间。因此,您可以创建一个对象向量并且效率将保持完全足够的可能性非常大。在这种情况下,你的循环变成(最多)agents.clear()(即使真的不需要,也很有可能)。

如果您处于相对罕见的情况,其中复制确实存在问题,那么您可能希望(例如)将agents定义为vector<shared_ptr<agent> >而不是使用原始指针。同样,不再需要删除指针对象,因为shared_ptr将在对象的引用计数达到0时自动处理。

对于C ++ 11,您可以(通常)通过支持移动语义而不是复制来完成同样的事情。假设您可以依靠所有编译器的支持,这可以进一步提高简单性和效率。

这可能不是一个详尽的可能性列表,其他一些可能会导致稍微不同的治疗方法。然而,重点仍然是:找出真正错误的东西,然后解决这个问题。

但是,我会再说一遍:现在,你正在寻找一个症状。你需要修复这种疾病,这会使症状消失。

编辑:像往常一样,有人似乎误解了(或者无法理解)向量是如何工作的。为了论证,我将使用他的64K元素的例子。为了使数学尽可能简单,我将进一步假设向量完全填满,并且在需要调整大小时,此实现精确地使向量的大小加倍。

在这种情况下,从未复制过32K的元素。另一个16K已被复制一次。另外8K已被复制两次。另外4K已被复制三次,2K四次,1K五次,512次六次,256次七次,128次八次,64次九次,32次十一次,十一次十一次,八次十二次,四次十三次,二次十四次十一次和十五次(当然,实际上,实际的实现可能从至少8个或10个元素开始,尽管它没什么区别)。将该总和除以64K得出元素被复制的平均次数:

所以,我们得到的是:

  32K* 0
 +16K* 1
 + 8k* 2
 + 4K* 3
 + 2K* 4
 + 1K* 5
 +512* 6
 +256* 7
 +128* 8
 + 64* 9
 + 32* 10
 + 16* 11
 +  8* 12
 +  4* 13
 +  2* 14
 +  1* 15
 =  65519

除以65536得出向量中元素的平均复制次数 - 0.999741。如果我们只添加一个元素,我们会再次复制每个元素,并且只有一个元素被复制了0次。这给了我们平均1.99971。

我怀疑需要大量的想象才能找出那些的上限和下限:分别为1和2。

实际上,很少有实现以这种方式工作。大多数设置较大的最小尺寸(通常类似于10或20个元素),并且大多数使用较小的增长因子。较大的最小尺寸减少了许多实际尺寸的实际副本数量。较小的生长因子会增加上限 - 但(重要的是)上限仍然是一个常数。

答案 1 :(得分:2)

我会输入std::vector<Agent*>

答案 2 :(得分:2)

在C ++ 11中,您可以执行range based for循环

map<string, string> address_book;
for ( auto address_entry : address_book )
{
  cout  << address_entry.first << " < " << address_entry.second << ">" << endl;
} 

答案 3 :(得分:2)

#include <algorithm>

void delete_(Agent* agent) {
    delete agent;
}
...

std::for_each(agents.begin(), agents.end(), delete_);

答案 4 :(得分:1)

提升你可以考虑的东西吗? 在这种典型的“for each”循环中,您可以使用Boost的foreach:

#include <boost/foreach.hpp>

BOOST_FOREACH(Agent* a, agents) {
    delete a;
}

http://www.boost.org/doc/libs/1_48_0/doc/html/foreach.html

答案 5 :(得分:0)

std::vector<Agent*>::const_iterator iter = agents.begin(); 


 while( iter != agents.end() )
 {
     delete * iter;
     iter++;
 }

答案 6 :(得分:0)

使用Boost Foreach和#define使其更漂亮。

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

...

foreach(Agent* p, agents)
{
  delete p;
}

答案 7 :(得分:0)

好吧,我的眼睛流血,因为这条线超过80列:)

试试这个:

std::vector<Agent*>::const_iterator iter;
for (iter = agents.begin(); iter != agents.end(); ++iter)
{
    delete *iter;
}

答案 8 :(得分:0)

C ++ 2011是你的朋友:

std::for_each(agents.begin(), agents.end(),
                      [](Agent* agent){ delete agent; });

答案 9 :(得分:0)

我怀疑他/她在抱怨明确写出STL范围的for循环。还有其他选择,例如来自std::for_each的{​​{1}}。使用较新的编译器,您还可以使用<algorithms>和lambda表达式来更简洁地编写它。