通过调用vector.size()完成了多少工作?

时间:2012-01-06 11:33:35

标签: c++

这是一个简单的吸气剂吗?或者每次计算?

如果我有这样的for循环:

for (int i = 0; i < myVector.size(); i++) { }

我只需要使用一次大小,在循环之前计算一次并将其存储在变量中会更好吗?或者size()只是一个简单的吸气剂,它会有什么不同?

7 个答案:

答案 0 :(得分:6)

这是实现定义的。假设实现想要提供有效的标准库,那么两个最可能的实现是:

  • 从存储结束后指向一个指针的指针中减去指向存储开头的指针,并返回该计算的结果。
  • 返回一个大小变量,该变量对于向量中的每个操作保持同步。

无论如何,标准要求复杂性是恒定的,你不应该真的担心它。此外,编译器经常进行优化,以便自行存储大小。

答案 1 :(得分:5)

使用迭代器而不是这样的循环来获得更大的灵活性和基因性:

std::vector<int>::const_iterator iter = myVector.begin();

for(iter; iter!= myVector.end(); ++iter)
{

}

这确保了如果在开发的后期更改容器,则代码的耦合程度会降低。

答案 2 :(得分:1)

在我看过的每一个实现中,它都是每次计算的 - 在O(1)中 - 通过从结尾开始减去。所以,它几乎没有什么区别。编译器可能会或可能不会优化“计算”,但时间是如此微不足道,以至于除了最绝望的情况之外的所有人都无关紧要。

使用你认为更可维护的东西:无论读取效果如何,最好地定位逻辑等等。可以说值得注意的是,在使用size()时,即使内部有删除或插入,也要确保准确跟踪向量的结尾循环体,因此可能有功能上的原因要求(或避免)重复调用size()。

请注意,对于STL列表,size()不是常量时间 - 它是线性的。

答案 3 :(得分:1)

http://www.cplusplus.com/reference/stl/vector/size/表明操作的复杂性是不变的。这意味着每次调用size()时都不会重新计算大小,或者在大多数情况下开销可以忽略不计。所以在你的代码中使用它就好了:这不会是你代码的瓶颈。 (除非这是一个紧密的内循环,每次优化都会带来很多。)

此外,如果要在循环内更改矢量,您将会更安全(但不是完全安全!)。

答案 4 :(得分:0)

如果你看一下vector :: size()的实现

size_type size() const
{   // return length of sequence
        return (_Mylast - _Myfirst);
}

其中_Mylast和_Myfirst是指针。

它只是一个指针算术,应该是一个原子操作。因此它不应该是一个开销!

答案 5 :(得分:0)

也许您实际上想知道编译器是否可以确定size()函数返回的值永远不会改变并且将其提升它。虽然这看似合理,但编译器必须能够证明提升产生与您编写的版本完全相同的行为(即“每次都调用”size()),并且有限制编译器可以看到多远,或者实际上可以证明这有多远。

因此,如果您知道没有更改容器的大小,则应手动编写提升机:

// index version:
for (std::size_t i = 0; end = v.size(); i != end; ++i) { work_with(v[i]); }

// iterator version:
for (auto it = v.begin(), end = v.end(); it != end; ++it) { work_with(*it); }

std::vector::size()的调用具有恒定的复杂性,即它们的成本与向量中的元素数量无关,但仍可能存在成本。在最好的情况下,你只是读取一个变量(所以在一个副本的提升和非提升版本的循环中没有区别),但是实现执行指针减法也是可行的。 / p>

答案 6 :(得分:0)

size()计算std::vector需要恒定的时间,但计算可能比两个指针之间的差异更大。虽然这似乎是一种廉价的计算,但它增加了对可用寄存器的限制。此外,std::vector的其他实现在size()中需要付出更多努力,但会使其他操作更有效。

例如,对于许多用例,最好只用一个指向范围起点的指针表示std::vector,并将控制数据放在此范围之前。奇怪的是,这种情况发生在使用数组分配分配的内存通常是如何布局的。控制数据将包含两个指针(一个指向范围的末尾,另一个指向容量)。在这种情况下获得大小包括地址调整(虽然具有固定大小),指针取消引用和减法。仍然不是很多但是你在[紧]循环中做的越少,它运行得越快(在大多数情况下)。

结果是:除非容器正在改变其大小,否则只需获取size()end()一次。如果它正在改变大小,它取决于容器的类型是否应该重新计算东西:例如对于std::list你不需要再次获得end()(并且你根本不需要得到它的size()。)我似乎记得斯科特迈耶斯在“有效STL”中讨论过这个话题,但我不记得他是否说过我刚做过的事(或者他是不是错了;)。