#c #arrays
#c #массивы
Вопрос:
Я отчаянно ищу информацию в сети и на этом сайте, чтобы понять, как делать то, что я хочу делать. И хотя есть множество результатов поиска, я не уверен, действительно ли я понимаю, как это сделать (я довольно новичок в C ). Так что я надеюсь, что здесь мне помогут. Что я хочу сделать: я хочу создать контур из геометрических сегментов, например, линейный сегмент, за которым следует сегмент дуги окружности, за которым следуют три линии, за которыми следует все, что может появиться. Порядок и определение каждого сегмента, составляющего контур, могут варьироваться от исполнения к исполнению. Геометрические элементы уже созданы. Теперь я хочу собрать их вместе. Я совершенно не уверен, какой путь избрать:
- Моей первой идеей было использовать связанный список, так как позже я хочу просмотреть каждый сегмент в том же порядке (чтобы не переходить от 3-го сегмента к 9-му сегменту и обратно к 5-му сегменту). 1a) Как я могу реализовать это при условии, что классы для каждого сегмента сильно отличаются. 1b) Как я могу получить доступ к определенному элементу? Нужно ли мне определять идентификационный номер для каждого участника списка, а затем проходить по списку, пока я не дойду до этого идентификационного номера?
- Более удобным, вероятно, был бы массив. Но я все еще не понимаю, как это сделать правильно.
Было бы здорово, если бы кто-нибудь здесь мог указать, как подойти к этому. Спасибо!
Вот упрощенные классы, с которыми я имею дело (круг здесь представляет дугу окружности):
struct point {
double x;
double y;}
class Line
{
public:
Line(const point pt1, const point pt2)
{
P1.x = pt1.x;
P1.y = pt1.y;
P2.x = pt2.x;
P2.y = pt2.y;
}
~Line() {};
double get_length() { return calc_length(); }
double get_angle() { return angle; }
private:
point P1;
point P2;
double calc_length()
{
// calculate length (here: dummy value)
length = 1;
}
double calc_angle()
{
// calculate angle (here: dummy value)
angle = 0.5;
}
double length = 0;
double angle = 0;
}
class circle
{
public:
circle(const double r, const point c)
{
radius = r;
center.x = c.x;
center.y = c.y;
}
~circle() {};
double get_radius() { return radius; };
point get_center() { return center; };
double get_circumference() { return 3.14 * radius; };
private:
double radius;
double circumference = 0;
point center;
}
Комментарии:
1. Если все они наследуются от одного и того же базового класса (например
Shape
, ), у вас может быть список (или массив, или вектор …) фигур. Это именно тот сценарий, в котором объектно-ориентированное программирование преуспевает. И если вам нужно получить доступ к элементам не по порядку, лучше использовать массив (или вектор, если вы не уверены в размере).2. Похоже, вы действительно описываете путь . Если вы действительно не хотите разделить его на разные виды фигур, а затем попытаться соединить их вместе, сопоставив концы дуги окружности с сегментами линий и т. Д. … учтите, что особые случаи сегментов сплайнов Безье могут описывать окружность или прямую линию.
3. Спасибо @Amadan: да, массив будет в порядке, так как количество сегментов будет определено во время выполнения, поэтому моя идея состоит в том, чтобы иметь указатель на массив [n] для этого.
4. Спасибо @paddy: ты подаешь мне идею! Это путь, но я бы постарался избежать использования для этого сплайна Безье. Я могу исследовать, может ли быть выполнима дискретизация, например, дуги в многоугольник.
Ответ №1:
Контейнеры C хранят элементы одного и того же типа, независимо от типа контейнера — оба vector
и list
ведут себя одинаково в этом отношении — они содержат только элементы определенного типа.
Чтобы преодолеть это ограничение, вы должны использовать полиморфизм — тип элементов контейнера должен вести себя как Line
или circle
.
Один из способов сделать это — использовать std::variant
. Объявите свой контейнер следующим образом:
std::vector<std::variant<Line, circle>> my_list_of_shapes;
Чтобы добавить фигуру в список, используйте push_back
:
Line my_line(...);
circle my_circle(...);
my_list_of_shapes.push_back(my_line);
my_list_of_shapes.push_back(my_circle);
Чтобы извлечь фигуру из списка, обратитесь к списку с помощью индекса (или итератора) и используйте std::get
для преобразования std::variant
в ваш конкретный тип:
circle your_circle = std::get<circle>(my_list_of_shapes[1]);
Если вы пишете какой-то код, который перебирает контейнер и не «знает», что хранится в определенном месте — Line
или circle
— вы можете использовать std::holds_alternative
для проверки этого:
for (auto shape: my_list_of_shapes)
{
if (std::holds_alternative<Line>(shape)
{
std::cout << std::get<Line>(shape).get_angle();
}
else if (std::holds_alternative<circle>(shape)
{
std::cout << std::get<circle>(shape).get_radius();
}
}
Комментарии:
1. Я пытаюсь это реализовать, но мой код уже завершается с ошибкой при объявлении вектора. Я скопировал вашу строку в свой код и добавил два #includes (вектор и вариант), но я получаю следующее сообщение об ошибке «ожидается объявление».
2. Он вообще не работает: (Я понятия не имею, как это решить.
3. Вы можете опубликовать отдельный вопрос, если у вас есть какая-то проблема, которую вы не знаете, как решить. Либо это, либо надейтесь, что кто-то случайно найдет ваши комментарии в Интернете, и будьте готовы угадать, в чем ваша проблема (этого не произойдет). Если вы опубликуете новый вопрос, вы можете поместить ссылку на него здесь.
4. Хорошо, спасибо за ваш совет. Я здесь новичок 🙂