如何使用迭代器?

77

我正试图计算两个点之间的距离。在C++中,我将这两个点存储在一个向量中:(0,0)和(1,1)。

我应该得到以下结果:

0
1.4
1.4
0

但我得到的实际结果是

0
1
-1
0

我认为我在使用vector迭代器的方式上出现了问题。我该如何解决这个问题?

我下面发布了代码。

typedef struct point {
    float x;
    float y;
} point;

float distance(point *p1, point *p2)
{
    return sqrt((p1->x - p2->x)*(p1->x - p2->x) +
                (p1->y - p2->y)*(p1->y - p2->y));
}

int main()
{
    vector <point> po;
    point p1; p1.x = 0; p1.y = 0;
    point p2; p2.x = 1; p2.y = 1;
    po.push_back(p1);
    po.push_back(p2);

    vector <point>::iterator ii;
    vector <point>::iterator jj;
    for (ii = po.begin(); ii != po.end(); ii++)
    {
        for (jj = po.begin(); jj != po.end(); jj++)
        {
            cout << distance(ii,jj) << " ";
        }
    }
    return 0;
}
3个回答

211
你的代码能够编译可能是因为你在某个地方使用了using namespace std。(否则,vector将必须是std::vector。) 我建议不要这样做,你刚刚提供了一个很好的案例:
意外地,你的调用选择了std::distance(),它接受两个迭代器并计算它们之间的距离。删除使用指令,以及所有标准库类型前缀加上std::,编译器将告诉你尝试传递了一个需要point*而你却传递了一个vector <point>::iterator的参数。
要获取迭代器所指向对象的指针,您必须对迭代器进行解引用-这会给出对该对象的引用-然后取结果的地址:&*ii
(请注意,指针完全可以满足std::vector迭代器的所有要求,一些早期的标准库实现确实使用指针,这使您可以将std::vector迭代器视为指针。但是现代实现使用了一个特殊的迭代器类。我想原因是使用类允许为指针和迭代器重载函数。此外,使用指针作为std::vector迭代器会鼓励混合使用指针和迭代器,这将导致在更改容器时代码无法编译。)

但与其这样做,我建议您改变函数的方式,使其接受引用(请参见此答案,了解为什么这是一个好主意)。:

float distance(const point& p1, const point& p2)
{
    return sqrt((p1.x - p2.x)*(p1.x - p2.x) +
                (p1.y - p2.y)*(p1.y - p2.y));
}

注意,这些点是通过const引用传递的。这表明调用者函数不会更改传递给它的点。
然后你可以像这样调用它:distance(*ii,*jj)

顺便提一下,这个

typedef struct point {
    float x;
    float y;
} point;

在C++中,是否需要使用C-ism?只需将其拼写即可。

struct point {
    float x;
    float y;
};

那样做会导致问题,如果这个struct定义要从C编译器解析(代码必须引用struct point,而不仅仅是point),但我猜std::vector等会对C编译器构成更大的挑战。

18
这个答案是错误的。std::distance可以通过ADL在std::iterator上被捕获,因此它可能成为候选集的一部分,无论是否使用了“std”。 - Puppy
4
@Puppy:确实如此(2.5年来没有人注意到),但这并不是我回答的全部内容。将“const point&p1”作为参数传递也可以解决这个问题。 - sbi
5
不,这不会解决问题。仍然有可能错误地编写 distance(ii, jj) 而得到 std::distance - Ben Voigt
11
虽然没有人明确说过,但强制使用你所定义的 distance 函数最明显的方法是在调用函数时写成 ::distance(...) 而不是 distance(...)。由于你的 distance 函数是在全局命名空间中定义的,因此可以使用空前缀 ::distance 来限定函数名称(当然,在调用函数时必须解引用迭代器以正确调用函数)。 - ABu
5
"using namespace std;"是一种非常糟糕的做法。这就是我们要传达的思想。请帮忙宣传! - amanuel2
显示剩余3条评论

21

巧合的是,您实际上正在使用内置的STL函数"distance",它计算迭代器之间的距离,而不是调用自己的distance函数。 您需要"解引用"迭代器以获取包含的对象。

cout << distance(&(*ii), &(*jj)) << " ";

从上面的语法可以看出,“迭代器”与广义“指针”非常相似。 迭代器不能直接用作“你”的对象类型。 实际上,迭代器与指针非常相似,许多标准算法对迭代器的操作也适用于指针。

正如Sbi所指出的那样:您的距离函数使用指针。 最好将其重写为取const引用,这会使函数更符合C ++的“规范化”,并使迭代器解引用语法更加容易。

float distance(const point& i_p1, const point& i_p2)
{
    return sqrt((p1.x - p2.x)*(p1.x - p2.x) +
                (p1.y - p2.y)*(p1.y - p2.y));
}

cout << distance(*ii, *jj) << " ";

7

你可以尝试以下几个方法:

  1. Make the distance() function take references to point objects. This is really just to make things more readable when calling the distance() function:
    float distance(const point& p1, const point& p2)
    {
        return sqrt((p1.x - p2.x)*(p1.x - p2.x) +
                    (p1.y - p2.y)*(p1.y - p2.y));
    }
    
  2. Dereference your iterators when calling distance()so you're passing the point objects:
    distance( *ii, *jj)
    

    If you don't change the interface of the distance() function, you might have to call it using something like the following to get appropriate pointers:
    distance( &*ii, &*jj)
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接