Почему я не могу поместить этот объект в свой std :: list?

Только начал программировать на 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 нескалярного типа? Я просто пытаюсь создать новые точки и включить их в список здесь.


person KingNestor    schedule 11.02.2009    source источник
comment
Вы не получаете доступ к x и y через iter в вашем примере кода. Какую именно ошибку вы получаете?   -  person codelogic    schedule 11.02.2009
comment
@codelogic, да, я. currentPoint = * iter;   -  person KingNestor    schedule 11.02.2009
comment
Верно, чтобы вы не получили ошибку при доступе к x и y через iter (поскольку это не так, вы назначаете его currentPoint). Укажите точную ошибку компилятора.   -  person codelogic    schedule 11.02.2009
comment
@KingNestor Вы получаете доступ к x и y через currentPoint, а не через него. Вы делаете копию того, на что ссылается iter, через currentPoint = * iter. Это должно скомпилироваться.   -  person zdan    schedule 11.02.2009


Ответы (7)


Это должен быть допустимый фрагмент кода.

#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);

И тебе будет лучше с последним.

person jasedit    schedule 11.02.2009

несколько вещей..

  • Вы пробовали iter->x и iter->y вместо копирования значения?
  • указанную вами ошибку трудно понять. Вы не пытаетесь получить x и y через итератор, вы копируете данные итератора в новую точку.

РЕДАКТИРОВАТЬ:

по новой информации в ОП. Вы пытаетесь создать новый объект без указателя, а затем пытаетесь вставить точку в вектор, который принимает только объекты. Вам нужно либо сделать вектор вектором указателей и не забыть удалить их послесловия, либо создать новую точку в стеке и скопировать их в вектор со стандартным назначением. Попробуй это:

if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
    Point currentPoint = Point(x, y);
    pointList.push_front(currentPoint);
}
person Gregor Brandt    schedule 11.02.2009
comment
первый пункт в значительной степени спорный, поскольку стандарт диктует, что end () является операцией O (1). К тому же любой достойный компилятор может его неплохо оптимизировать. Пункт №2 - это настоящая проблема, которая у него есть. - person Evan Teran; 11.02.2009
comment
компилятор не может оптимизировать его, потому что он не знает, что вызов функции возвращает константу (в этом случае). Так что, по крайней мере, он должен выполнить вызов функции, потратив на ветвление и возврат, а также на манипуляции со стеком. Это может быть дорогостоящим в зависимости от облицовки труб ЦП. - person Gregor Brandt; 11.02.2009
comment
да, это так, потому что функция является константной, что означает, что она не меняет состояние объекта. - person Evan Teran; 11.02.2009
comment
Я рекомендую вам посмотреть на сборочный вывод предварительного расчета и сравнить с ним, вы обнаружите, что они более или менее идентичны. - person Evan Teran; 11.02.2009
comment
возврат размера не является константой, просто определение размера метода является константой, это означает, что класс является константным, безопасным для этого вызова. т.е. константный вектор x; x.size - это законный вызов. Вызов функции все равно будет выполнен, поскольку возвращаемые данные не являются константными. - person Gregor Brandt; 11.02.2009
comment
end ()! = size (), если вы действительно протестируете, то обнаружите, что пока вы не изменяете контейнер, компилятор будет достаточно умен, чтобы не вызывать его каждый раз. Фактически, в большинстве случаев он будет полностью встроен. - person Evan Teran; 11.02.2009
comment
Следующие две программы создали идентичную сборку с g ++ -O3: rafb.net/p /e32cuR54.html. Проверьте свои теории, прежде чем утверждать, что они верны. - person Evan Teran; 11.02.2009
comment
GCC 4.01 с g ++ -O3 -S производит разные выходные данные сборки и разные двоичные файлы с векторами и списками. GCC 4.2 производит идентичный вывод для списков, но другой вывод для векторов. ОП использует вектор, мой комментарий стоит. - person Gregor Brandt; 11.02.2009
comment
Прежде всего, OP использовал std :: list, а не std :: vector. Во-вторых, вы обнаружите, что даже если они разные, разница небольшая и это не повторный вызов функции end (). Ваш комментарий по-прежнему неверен. - person Evan Teran; 11.02.2009
comment
Если вы прочитаете заголовок, реализующий end (), вы увидите: iterator end () {return & this - ›_ M_impl._M_node; } или: iterator end () {вернуть итератор (это - ›_ M_impl._M_finish); } Не может быть ничего, кроме O (1). - person jasedit; 11.02.2009
comment
Ваше последнее изменение содержит ключевую информацию. К сожалению, мой +1 довел этот ответ только до 0 ... - person j_random_hacker; 11.02.2009

Если у вас уже есть функция, которую вы хотите применить ко всему списку, 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 ++, из-за возвращаемых типов (ссылка vs значение / копия)
person maccullt    schedule 11.02.2009
comment
Это хороший ответ, помогающий мне с моей итерацией, спасибо - person KingNestor; 11.02.2009

Нескалярная проблема заключается в том, что вы назначаете указатель Point (возвращаемое значение оператора new) объекту стека Point (поскольку он не Point * в вашем коде).

Я бы порекомендовал сказать

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

Обратите внимание, что currentPoint будет скопирован в ваш список; неявно сгенерированный конструктор копирования Point (поскольку вы не объявили конструктор Point (const Point & other) в своем классе, компилятор сделал его за вас) скопирует currentPoint.x и currentPoint.y в список; в этом случае это нормально. Точка маленькая, поэтому затраты на копирование невелики, и она содержит только два целых числа, поэтому прямое копирование целых чисел нормально.

person Matt    schedule 11.02.2009

Этот ответ относится к отредактированной версии вопроса.

Как сказал gbrandt в отредактированной версии своего ответа, ваша проблема в том, что вы пытаетесь динамически выделить экземпляр Point, а затем назначить его Point объекту, а не указателю на Point. Результатом new является указатель на Point, а не объект Point - в данном случае вам действительно нужен последний, который вы создаете без new:

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

Поскольку list<T>::push_front() помещает копию объекта Point в список, вам не нужно выполнять здесь какое-либо динамическое размещение. Гораздо безопаснее избегать динамического распределения, когда это возможно, поскольку это может легко привести к утечке памяти - например, следующий альтернативный код, который компилируется и работает, приводит к утечке памяти, поскольку объект, на который указывает currentPoint, никогда не является deleted:

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

Конечно, вы можете просто добавить delete currentPoint; в конец, чтобы устранить утечку, но зачем использовать медленное динамическое выделение, если выделение на основе стека делает работу быстрее и проще?

person j_random_hacker    schedule 11.02.2009

Вот как я обычно подхожу к таким циклам, если вы не хотите использовать std :: foreach:

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

Будьте осторожны с этими моментами:

  • pointListObject - это экземпляр pointList; если вы используете класс (тип pointList, а не экземпляр pointList), вы попадаете в беду, но компилятор будет сильно ныть. То же и с iter. Это просто упрощает поиск, если вы разделяете имена типов и имена экземпляров.
  • Подобная совместная инициализация итераторов позволяет сохранить инициализацию end внутри цикла (хорошо для определения области видимости), сохраняя при этом дешевизну выполнения для каждого цикла.
person Matt    schedule 11.02.2009

Вы вырезали и вставляли этот код в SO из своего файла .cpp или перепечатывали его? Судя по звуку вашего сообщения об ошибке, в исходном коде написано

glVertex2i(iter.x, iter.y);

Что, как указал Гбрандт, не разыменовывает итератор должным образом.

Я бы переписал цикл следующим образом:

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 () ровно один раз, используйте преинкремент и разыменуйте итератор один раз в константную ссылку. Таким образом, у вас не будет копирования, когда ваш исходный код скопировал объект Point, на который ссылается * iter, и вы избежите разыменования итератора дважды, примерно так, как вы можете получить здесь.

Теперь, в качестве непредвиденного совета OpenGL, я бы также указал, что массивы вершин, вероятно, являются лучшим выбором, чем вызовы немедленного режима (glVertex *).

Надеюсь это поможет...

person Drew Hall    schedule 11.02.2009