使用向量时使用无符号整数或迭代器? 我目前正在与一些朋友一起做C++学校项目。 以前,当我在C++中有向量时,我做了类似的事情来使用它们: unsigned int i = 0; while (i != myVector.size()) { doSomething(myVector[i]); i++; }

使用向量时使用无符号整数或迭代器? 我目前正在与一些朋友一起做C++学校项目。 以前,当我在C++中有向量时,我做了类似的事情来使用它们: unsigned int i = 0; while (i != myVector.size()) { doSomething(myVector[i]); i++; },c++,iterator,C++,Iterator,但是在这个项目中,我的朋友们看到我使用这样的向量并不高兴,他们让我使用迭代器。 我不太喜欢迭代器,因为它们的语法很难记住,但我的朋友说使用迭代器更好,因为它工作得更快。 因为我们在一个有很多向量的大项目中工作,所以使用迭代器是至关重要的 时间已经过去,即使我仍然记不起它们的语法,我仍然在使用它们,但我想看看迭代器方法是否真的比无符号int方法快 所以我做了两个节目: 使用unsigned int方法的第一个程序: #include <vector> #include <stri

但是在这个项目中,我的朋友们看到我使用这样的向量并不高兴,他们让我使用迭代器。 我不太喜欢迭代器,因为它们的语法很难记住,但我的朋友说使用迭代器更好,因为它工作得更快。 因为我们在一个有很多向量的大项目中工作,所以使用迭代器是至关重要的

时间已经过去,即使我仍然记不起它们的语法,我仍然在使用它们,但我想看看迭代器方法是否真的比无符号int方法快

所以我做了两个节目:

使用unsigned int方法的第一个程序:

#include <vector>
#include <string>
#include <iostream>

int main()
{
    std::string str = "This is a string";

    int i = 0;
    std::vector<std::string> vec;

    while (i != 10000000)
    {
        vec.push_back(str);
        i++;
    }

    unsigned int j = 0;
    while (j != vec.size())
    {
        std::cout << vec[j] << std::endl;
        j++;
    }
    return (0);
}
real    0m39,391s
user    0m5,463s
sys     0m21,108s
结果如下:

无符号int方法:

#include <vector>
#include <string>
#include <iostream>

int main()
{
    std::string str = "This is a string";

    int i = 0;
    std::vector<std::string> vec;

    while (i != 10000000)
    {
        vec.push_back(str);
        i++;
    }

    unsigned int j = 0;
    while (j != vec.size())
    {
        std::cout << vec[j] << std::endl;
        j++;
    }
    return (0);
}
real    0m39,391s
user    0m5,463s
sys     0m21,108s
迭代器方法:

#include <vector>
#include <string>
#include <iostream>

int main()
{
    std::string str = "This is a string";

    int i = 0;
    std::vector<std::string> vec;

    while (i != 10000000)
    {
        vec.push_back(str);
        i++;
    }

    std::vector<std::string>::iterator it;
    it = vec.begin();
    while (it != vec.end())
    {
        std::cout << *it << std::endl;
        it++;
    }
    return (0);
}
real    0m39,436s
user    0m5,972s
sys     0m20,652s
还有。。。。。。。。。是同一时间?! 两者之间只有不到1秒的微小差别,这是一个包含1000万个字符串的向量


因此,我想知道这两种方法之间是否真的存在差异,迭代器是否真的更好使用?

令人惊讶的是,在循环中比较迭代器访问和索引访问时,可能至少存在理论性能差异,如下所示:

使用迭代器过滤一些噪声,每个迭代看起来像

.LBB0_1:                                # =>This Inner Loop Header: Depth=1
    movl    (%rbx), %edi
    callq   check(int)
    addq    $4, %rbx
    cmpq    %rbx, %r14
    jne     .LBB0_1
这里我们看到一个内存访问,一个数学运算和一个条件分支。总的来说,一旦您离开缓存线,内存访问将使其他所有操作相形见绌,但这些操作仍然需要执行

当我们研究索引访问时,迭代看起来像:

.LBB1_3:                                # =>This Inner Loop Header: Depth=1
    movq    (%r14), %rax
    movl    (%rax,%rbx,4), %edi
    callq   check(int)
    addq    $1, %rbx
    cmpq    %r15, %rbx
    jb      .LBB1_3
在这里,我们看到了在上一个示例中没有看到的东西——位移内存访问所需的每次迭代都会有一个额外的寄存器移动

现在,寄存器移动可能是CPU可以执行的最便宜的实际操作之一,但它仍然是一个操作,而且它将是一个重新排序的块,因为以后的操作取决于它的结果

我相信,我们在这里看到的性能影响不应该是访问向量时所考虑的。相反,您应该追求一致性、易读性和可维护性

说到这里,我建议您选择基于范围的循环

for (int i: vec) {
     // work with i
}

最后,但不是列表,使用unsigned int变量迭代向量的索引可能是一个令人讨厌的错误。在许多平台上,vector可能比maxim int允许的要大,最终会导致无止境的循环。

令人惊讶的是,在循环中比较迭代器访问和索引访问时,可能至少存在理论性能差异,如下所示:

使用迭代器过滤一些噪声,每个迭代看起来像

.LBB0_1:                                # =>This Inner Loop Header: Depth=1
    movl    (%rbx), %edi
    callq   check(int)
    addq    $4, %rbx
    cmpq    %rbx, %r14
    jne     .LBB0_1
这里我们看到一个内存访问,一个数学运算和一个条件分支。总的来说,一旦您离开缓存线,内存访问将使其他所有操作相形见绌,但这些操作仍然需要执行

当我们研究索引访问时,迭代看起来像:

.LBB1_3:                                # =>This Inner Loop Header: Depth=1
    movq    (%r14), %rax
    movl    (%rax,%rbx,4), %edi
    callq   check(int)
    addq    $1, %rbx
    cmpq    %r15, %rbx
    jb      .LBB1_3
在这里,我们看到了在上一个示例中没有看到的东西——位移内存访问所需的每次迭代都会有一个额外的寄存器移动

现在,寄存器移动可能是CPU可以执行的最便宜的实际操作之一,但它仍然是一个操作,而且它将是一个重新排序的块,因为以后的操作取决于它的结果

我相信,我们在这里看到的性能影响不应该是访问向量时所考虑的。相反,您应该追求一致性、易读性和可维护性

说到这里,我建议您选择基于范围的循环

for (int i: vec) {
     // work with i
}

最后,但不是列表,使用unsigned int变量迭代向量的索引可能是一个令人讨厌的错误。在许多平台上,vector可能比maxim int允许的要大,最终会导致无休止的循环。

使用迭代器的主要原因不是性能,而是错误可能性较小,代码更具表现力。比较一下这个

unsigned int i = 0;
while (i != myVector.size())
{
    doSomething(myVector[i]);
    i += 2;
}

基于范围的for循环使迭代器的使用尽可能简单,因为你甚至看不到迭代器,但它们是在幕后使用的。当您手动管理索引时,有数百万种错误的方法,使用迭代器可能有2或3种

为了进行性能比较:当向量将其元素存储在连续内存中时,向量迭代器可以是普通指针。您认为的开销主要是语法上的糖分,使您能够编写更好的代码。因此,你看不出有多大的不同也就不足为奇了

PS

我经常使用它,我有信心不会犯太多错误

使用整数迭代数组是上个世纪的事。它不安全,导致很难检测到bug,并且很容易调用未定义的行为。编写代码来表达您想要做的事情,而不是指导处理器。如果你想 o为向量的每个元素做一些事情,您应该为循环或更早的元素创建一个范围:


它没有手动使用索引的任何缺点。您是否发现了上述循环中的错误?无论myVector实际上是什么容器,或者它包含什么类型的元素,或者它实际上是什么类型的,它都具有相同的外观。它可以是一个自由函数、一个函子、一个lambda,由您选择。

使用迭代器的主要原因不是性能,而是错误的可能性更小,代码更具表现力。比较一下这个

unsigned int i = 0;
while (i != myVector.size())
{
    doSomething(myVector[i]);
    i += 2;
}

基于范围的for循环使迭代器的使用尽可能简单,因为你甚至看不到迭代器,但它们是在幕后使用的。当您手动管理索引时,有数百万种错误的方法,使用迭代器可能有2或3种

为了进行性能比较:当向量将其元素存储在连续内存中时,向量迭代器可以是普通指针。您认为的开销主要是语法上的糖分,使您能够编写更好的代码。因此,你看不出有多大的不同也就不足为奇了

PS

我经常使用它,我有信心不会犯太多错误

使用整数迭代数组是上个世纪的事。它不安全,导致很难检测到bug,并且很容易调用未定义的行为。编写代码来表达您想要做的事情,而不是指导处理器。如果要对向量的每个元素执行某些操作,则应使用基于范围的for循环或更旧的循环:


它没有手动使用索引的任何缺点。您是否发现了上述循环中的错误?无论myVector实际上是什么容器,或者它包含什么类型的元素,或者它实际上是什么类型的元素,它都具有相同的外观。它可以是自由函数、函子、lambda,由您选择。

请确保运行任何经过充分优化的基准测试。我不希望这两者之间有什么大的区别,但是养成使用迭代器的习惯是很好的,因为所有容器都有迭代器,但并非所有容器都允许通过索引进行随机访问。decltypemyVector.size将始终获得正确的类型。。另外,const auto&element:myVector也是一个不错的选择。related/dupe:。如果您不需要索引,只需使用基于范围的for循环。迭代器背后的理论是可靠的,并且是构成标准库容器的基础,暴露迭代器可与算法一起通用。C++正在以更基于范围的方向YAY移动,因为你经常想要迭代整个事物的序列,而范围本身可以天真地认为是迭代器组成的。最后,编译器是智能的,它将优化同一程序集的所有不同方式。所以,做最适合当前问题的事情。在这些代码示例中,访问方法的任何性能差异都将被向量和I/O的创建所淹没。这些数字并没有说明任何有用的内容。请确保运行任何具有完全优化的基准测试。我不希望这两者之间有什么大的区别,但是养成使用迭代器的习惯是很好的,因为所有容器都有迭代器,但并非所有容器都允许通过索引进行随机访问。decltypemyVector.size将始终获得正确的类型。。另外,const auto&element:myVector也是一个不错的选择。related/dupe:。如果您不需要索引,只需使用基于范围的for循环。迭代器背后的理论是可靠的,并且是构成标准库容器的基础,暴露迭代器可与算法一起通用。C++正在以更基于范围的方向YAY移动,因为你经常想要迭代整个事物的序列,而范围本身可以天真地认为是迭代器组成的。最后,编译器是智能的,它将优化同一程序集的所有不同方式。所以,做最适合当前问题的事情。在这些代码示例中,访问方法的任何性能差异都会被向量和I/O的创建所淹没。这些数字没有任何用处。我会和我的朋友们谈论它,但我会在小组项目中试着使用迭代器,这样其他成员会很高兴,但是在一个单独的项目中,我可能会使用我的unsigned int方法,我真的很喜欢它,因为我经常使用它,所以我有信心不会犯太多错误。谢谢你的澄清answer@electo在某种程度上,您应该看看标准库中的算法。一旦你这样做了,你就无法适应iterators@electo使用无符号整数在向量的索引上进行迭代可能是一个错误。不要养成在索引迭代循环中使用有符号整数的习惯,即使对于示例也是如此。这可能会产生惊人的效果。@SergeyA hm给出一个不好的例子是有意的,但你是对的,这个例子也有足够的错误
在小组项目中工作时,我会尝试使用迭代器,这样其他成员会很高兴,但是在一个单独的项目中,我可能会使用我的unsigned int方法,我真的很喜欢它,因为我经常使用它,我有信心不会犯太多错误。谢谢你的澄清answer@electo在某种程度上,您应该看看标准库中的算法。一旦你这样做了,你就无法适应iterators@electo使用无符号整数在向量的索引上进行迭代可能是一个错误。不要养成在索引迭代循环中使用有符号整数的习惯,即使对于示例也是如此。这可能会产生惊人的效果。@SergeyA hm给出一个不好的例子是有意为之的,但你是对的,对于未签名的例子也是错误的