【问题标题】:Why can't I push this object onto my std::list?为什么我不能将此对象推送到我的 std::list 上?
【发布时间】:2010-10-06 19:40:45
【问题描述】:

刚开始用 C++ 编程。

我已经创建了一个 Point 类、一个 std::list 和一个迭代器,如下所示:

class Point { 
public:
    int x, y;
    Point(int x1, int y1)
    {
        x = x1;
        y = y1;
    }
};

std::list <Point> pointList;
std::list <Point>::iterator iter;

然后我将新点推送到 pointList 上。

现在,我需要遍历 pointList 中的所有点,所以我需要使用迭代器进行循环。这就是我搞砸的地方。

for(iter = pointList.begin(); iter != pointList.end(); iter++)
{
    Point currentPoint = *iter;
    glVertex2i(currentPoint.x, currentPoint.y);
}


更新

你们是对的,问题不在于我迭代列表。看来问题出在我试图将某些内容推送到列表时。

确切的错误:

mouse.cpp:在函数 void mouseHandler(int, int, int, int)': mouse.cpp:59: error: conversion fromPoint*' 中请求非标量类型 `Point'

这些行是:

 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
    Point currentPoint = new Point(x, y);
    pointList.push_front(currentPoint);

}

Point* 到非标量类型 Point 之间的转换是什么?我只是想创建新点并将它们推到这里的列表中。

【问题讨论】:

  • 您没有在示例代码中通过 iter 访问 x 和 y。你得到的确切错误是什么?
  • @codelogic,是的,我是。 currentPoint = *iter;
  • 对,所以你不会得到关于通过 iter 访问 x 和 y 的错误(因为你不是,你将它分配给 currentPoint)。请提供确切的编译器错误。
  • @KingNestor 您正在通过 currentPoint 而不是 iter 访问 x 和 y。您正在通过 currentPoint = *iter 复制 iter 所引用的内容。这应该可以编译。

标签: c++ linked-list loops


【解决方案1】:

这应该是一段有效的代码。

#include <iostream>
#include <list>

class Point { 
public:
    int x, y;
    Point(int x1, int y1)
    {
        x = x1;
        y = y1;
    }
};

int main()
{
    std::list<Point> points;

    points.push_back(Point(0, 0));
    points.push_back(Point(1, 1));
    points.push_back(Point(2, 2));

    std::list<Point>::iterator iter;

    for(iter = points.begin(); iter != points.end(); ++iter)
    {
        Point test = *iter;
        std::cout << test.x << ", " << test.y << "; ";
    }
    std::cout << std::endl;

    return 0;
}

使用此代码:

jasons-macbook41:~ g++ test.cpp
jasons-macbook41:~ ./a.out
0, 0; 1, 1; 2, 2; 
jasons-macbook41:~ 

虽然我不会像您的代码那样创建 Point 的临时副本。我会像这样重写循环:

for(iter = points.begin(); iter != points.end(); ++iter)
{
    std::cout << iter->x << ", " << iter->y << "; ";
}

迭代器在语法上类似于指针。

编辑: 鉴于您的新问题,请从施工线上删除“新”。那是创建一个指向 Point 的指针,而不是堆栈上的 Point。这将是有效的:

Point* temp = new Point(0, 0);

或者这个:

Point temp = Point(0, 0);

你最好选择后者。

【讨论】:

    【解决方案2】:

    一些事情..

    • 您是否尝试过 iter-&gt;xiter-&gt;y 而不是复制值?
    • 您提到的错误很难理解。您不是试图通过迭代器获取 x 和 y,而是将迭代器数据复制到新点。

    编辑:

    根据 OP 中的新信息。您正在尝试新建一个非指针对象,然后尝试将该点填充到仅接受对象的向量中。您要么必须使向量成为指针向量并记住在后面删除它们,要么在堆栈上创建新点并使用标准分配将它们复制到向量中。试试这个:

    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
        Point currentPoint = Point(x, y);
        pointList.push_front(currentPoint);
    }
    

    【讨论】:

    • 第一点几乎没有实际意义,因为标准规定 end() 是一个 O(1) 操作。再加上每个像样的编译器都会很好地优化它。第 2 点是他的真正问题。
    • 编译器无法优化它,因为它不知道函数调用返回一个常量(在这种情况下)。所以至少它必须进行函数调用,花费一个分支、返回和堆栈操作。这可能会很昂贵,具体取决于 CPU 管道。
    • 是的,因为函数是 const 意味着它不会改变对象的状态。
    • 我建议你看一下预先计算它的汇编输出并与不比较,你会发现它们或多或少是相同的。
    • 返回的 size 不是 const,只是方法 size 的定义是 const,这意味着这个类对于这个调用是 const 安全的。即常量向量x; x.size 是合法调用。函数调用仍然会进行,因为它返回的数据不是 const
    【解决方案3】:

    如果您已经有一个想要应用于整个列表的函数,那么 std::for_each 就是要走的路,例如,

    std::for_each(pointList.begin(), pointList.end(), myGreatFunction);
    

    如果您必须编写 for 循环,则如下所示:

    std::list<Point>::iterator itEnd = pointList.end();
    for(std::list<Point>::iterator itCur=pointList.begin(); itCur != itEnd; ++itCur) {
        yourFunction(itCur->x, itCur->y);
    }
    

    注意事项:

    • ++itCur 可以比 itCur++ 更高效,因为返回类型(引用与值/副本)

    【讨论】:

    • 这是一个很好的答案,可以帮助我进行迭代,谢谢
    【解决方案4】:

    非标量问题是因为您将 Point 指针(operator new 的返回值)分配给 Point 堆栈对象(因为它在您的代码中不是 Point*)。

    我建议说

        Point currentPoint(x, y);
        pointList.push_front(currentPoint);
    

    请注意,currentPoint 将被复制到您的列表中; Point 的隐式生成的复制构造函数(因为您没有在类中声明 Point(const Point& other) 构造函数,编译器为您做了一个)会将 currentPoint.x 和 currentPoint.y 复制到列表中;在这种情况下,这很好。点小,所以复制开销低,而且它只包含两个int,所以直接复制int是可以的。

    【讨论】:

      【解决方案5】:

      此答案是指问题的编辑版本。

      正如 gbrandt 在其答案的编辑版本中所说,您的问题是您试图动态分配 Point 的实例,然后将其分配给 Point object 而不是指针 Pointnew 的结果是指向 Point 的指针,而不是Point 对象——在这种情况下,您真正​​想要的是后者,您在没有new 的情况下创建它:

      Point currentPoint(x, y);
      pointList.push_front(currentPoint);
      

      由于list&lt;T&gt;::push_front()Point 对象的副本 推送到列表中,因此您无需在此处进行任何动态分配。尽可能避免动态分配会更安全,因为它很容易导致内存泄漏——例如以下可编译和工作的替代代码会导致内存泄漏,因为currentPoint 指向的对象永远不是deleted:

      Point *currentPoint = new Point(x, y);
      pointList.push_front(*currentPoint);      // Notice the "*"
      

      当然,您可以在末尾添加delete currentPoint; 以消除泄漏,但是当基于堆栈的分配更快更简单时,为什么要使用慢速动态分配呢?

      【讨论】:

        【解决方案6】:

        如果您不想使用 std::foreach,以下是我通常处理此类循环的方式

        for (iter curr = pointListObject.begin(), end = pointListObject.end(); curr != end; ++curr)
        {
            glVertex2i(curr->x, curr->y);
        }
        

        注意以下几点:

        • pointListObject 是 pointList 的一个实例;如果你使用类(pointList 类型,而不是 pointList 的 instance),你会遇到麻烦,但编译器会抱怨很多。与 iter 相同。如果你将类型名和实例名分开,它只会让事情更容易理解。
        • 像这样对迭代器进行联合初始化可以让您将 end 的初始化保持在循环内(有利于范围界定),同时保持每个循环的执行成本较低。

        【讨论】:

          【解决方案7】:

          您是从 .cpp 文件中将此代码剪切并粘贴到 SO 中,还是重新键入?从您的错误消息的声音中,我猜原始代码说的是

          glVertex2i(iter.x, iter.y);
          

          正如 gbrandt 指出的那样,它没有正确取消引用迭代器。

          我会重写循环如下:

          std::list<Point>::const_iterator iter = pointList.begin();
          const std::list<Point>::const_iterator end = pointList.end();
          
          for (; iter != end; ++iter) {
            const Point& p = *iter;
            glVertex2i(p.x, p.y);
          } 
          

          主要更改是使用 const_iterators 而不是非常量,因为您的循环不打算修改列表内容。然后,只获取一次 begin() 和 end() 的值,使用 preincrement,并将迭代器解引用一次到 const 引用中。这样您就没有复制,您的原始代码复制了 *iter 引用的 Point 对象,并且您避免了两次取消引用迭代器以获得尽可能高的效率。

          现在,对于一些未经请求的 OpenGL 建议,我还要指出,顶点数组可能是比立即模式 (glVertex*) 调用更好的选择。

          希望这会有所帮助...

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-09-03
            • 1970-01-01
            • 2022-01-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多