C создает массив / связанный список с объектами разных классов

#c #arrays

#c #массивы

Вопрос:

Я отчаянно ищу информацию в сети и на этом сайте, чтобы понять, как делать то, что я хочу делать. И хотя есть множество результатов поиска, я не уверен, действительно ли я понимаю, как это сделать (я довольно новичок в C ). Так что я надеюсь, что здесь мне помогут. Что я хочу сделать: я хочу создать контур из геометрических сегментов, например, линейный сегмент, за которым следует сегмент дуги окружности, за которым следуют три линии, за которыми следует все, что может появиться. Порядок и определение каждого сегмента, составляющего контур, могут варьироваться от исполнения к исполнению. Геометрические элементы уже созданы. Теперь я хочу собрать их вместе. Я совершенно не уверен, какой путь избрать:

  1. Моей первой идеей было использовать связанный список, так как позже я хочу просмотреть каждый сегмент в том же порядке (чтобы не переходить от 3-го сегмента к 9-му сегменту и обратно к 5-му сегменту). 1a) Как я могу реализовать это при условии, что классы для каждого сегмента сильно отличаются. 1b) Как я могу получить доступ к определенному элементу? Нужно ли мне определять идентификационный номер для каждого участника списка, а затем проходить по списку, пока я не дойду до этого идентификационного номера?
  2. Более удобным, вероятно, был бы массив. Но я все еще не понимаю, как это сделать правильно.

Было бы здорово, если бы кто-нибудь здесь мог указать, как подойти к этому. Спасибо!

Вот упрощенные классы, с которыми я имею дело (круг здесь представляет дугу окружности):

 
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. Хорошо, спасибо за ваш совет. Я здесь новичок 🙂