迭代器的最佳编码风格



一名飞越者说这让他的眼睛流血:

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

写得更整洁的方法是什么?我想我可以对迭代器类型进行typedef,但在某些类中,这意味着顶部有一大块typedef。我认为这看起来很糟糕。

我会键入定义std::vector<Agent*>

在C++11中,您可以对循环进行基于范围的

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

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

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

如上所述,for循环是一种症状。疾病就是设计。当您修复设计时,您不必担心for循环的语法,因为您不再需要那个循环了。

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

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

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

这可能不是一个详尽的可能性列表,其他一些可能会导致略有不同的治疗方法。但问题仍然是一样的:找出真正的错误,并解决它。

然而,我要重复一遍:现在,你看到的是一种症状。你需要治好这种病,这样症状就会消失。

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

在这种情况下,32K的元素从未被复制过。另有16K已复制一次。另外8K已经复制了两次。另一个4K被复制了三次,2K四次,1K五次,512六次,256七次,128八次,64九次,32十次,16十一次,8十二次,4十三次,2十四次和1十五次(当然,在现实中,真正的实现可能至少从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个元素),大多数使用较小的生长因子。对于许多实际尺寸,较大的最小尺寸会减少实际副本数。较小的增长因子增加了上限——但(重要的)上限仍然是一个常数。

#include <algorithm>
void delete_(Agent* agent) {
    delete agent;
}
...
std::for_each(agents.begin(), agents.end(), delete_);

Boost是您可以考虑的吗?在这种典型的"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

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

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

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

试试这个:

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

使用Boost Foreach,并添加#define使其更加美观。

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
...
foreach(Agent* p, agents)
{
  delete p;
}

C++2011是您的朋友:

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

我怀疑他/她在抱怨为STL范围显式编写for循环。还有其他选择,比如<algorithms>中的std::for_each。使用较新的编译器,您还可以使用auto和lambda表达式来更简洁地编写它。

最新更新