Извините, если этот вопрос уже задавался, но я искал уже несколько часов и нахожу ответы только на похожие, но не совсем одинаковые проблемы, и ни один из них не работает в моем случае. Я начинающий программист, поэтому, скорее всего, мой код не работает либо из-за отвратительного дизайна, либо из-за очень простой глупой ошибки. У меня есть альтернативный дизайн, но я хотел бы знать, является ли дизайн буквально невозможным или я просто неправильно его реализую.
Итак, вот что я пытаюсь сделать: я пишу код обнаружения 2D-столкновений, который должен работать как для многоугольников, так и для кругов. Многоугольники должны обнаруживать столкновения с кругами и другими полигонами, а круги должны обнаруживать столкновения с полигонами и другими кругами. Я подумал, что было бы неплохо иметь возможность поместить все сталкивающиеся объекты (многоугольники и круги) в один std::vector под названием "Collidables", а не иметь отдельный вектор для каждого класса. Поэтому я создал абстрактный класс Shape, который станет родителем для Circle и Polygon и будет содержать виртуальные функции обнаружения столкновений. Тогда вектор Collidables будет последовательностью указателей Shape. Вот как выглядят эти классы:
//in shape.h:
#ifndef __SHAPE_H_INCLUDED__
#define __SHAPE_H_INCLUDED__
class Polygon;
class Circle;
class Shape {
//number of functions and variables irrelevant to the issue
//relevant functions, note that Vector with capital 'V' is not std::vector but a class I wrote myself:
virtual Vector* detectCollision(const Shape& Collidable) const = 0;
virtual Vector* detectCollision(const Polygon& Collidable) const = 0;
virtual Vector* detectCollision(const Circle& Collidable) const = 0;
};
#endif
//in polygon.h
#ifndef __POLYGON_H_INCLUDED__
#define __POLYGON_H_INCLUDED__
#include "shape.h"
//edit: deleted #include "circle.h", this is now included in polygon.cpp
//edit: deleted redundant Circle forward declaration
class Polygon: public Shape {
//irrelevant stuff
Vector* detectCollision(const Shape& Collidable) const {return Collidable.detectCollision(*this)};
Vector* detectCollision(const Polygon& Collidable) const;
Vector* detectCollision(const Circle& Collidable) const;
};
#endif
//in circle.h
#ifndef __CIRCLE_H_INCLUDED__
#define __CIRCLE_H_INCLUDED__
#include "shape.h"
//edit: deleted #include "polygon.h", this is now included in circle.cpp
class Circle: public Shape {
//irrelevant stuff
Vector* detectCollision (const Shape& Collidable) const {return Collidable.detectCollision(*this);}
Vector* detectCollision (const Polygon& Collidable) const;
Vector* detectCollision (const Circle& Collidable) const;
};
#endif
Идея здесь в том, что любая фигура должна иметь возможность обнаруживать столкновение с любой другой фигурой, даже не зная, обнаруживает ли она столкновение с многоугольником или с кругом во время вызова функции detectCollision. Представьте, что вы круг, и вы перебираете Collidables std::vector. Первый элемент — это Polygon, но вы этого не знаете, вы знаете только, что вы — круг и что параметр, который получает ваша функция detectCollision, является ссылкой на Shape. Итак, вы говорите этой форме: «Посмотри, я не знаю, кто ты, но я круг. Вот, я передам себя в качестве параметра твоей функции detectCollision, и ты сможешь вернуть мне результат». Таким образом, Shape вызывает свой виртуальный метод detectCollision(const &Circle), который затем передается в метод detectCollision(const &Circle) его дочернего элемента Polygon, который имеет фактическую рабочую реализацию и который должен затем вернуть указатель на точку пересечения ( или нулевой указатель, если нет пересечения).
Это работало нормально, когда были написаны только классы Shape и Polygon, но как только я добавил класс Circle, мой код больше не компилировался. Ошибка, которую я получаю:
В файле, включенном из polygon.cpp:1:0: polygon.h: В функции-члене 'виртуальный вектор* Polygon::detectCollision(const Circle&) const': polygon.h:45:78: ошибка: недопустимое использование неполного типа ' const struct Circle' shape.h:8:7: ошибка: предварительное объявление 'const struct Circle'
Я предполагаю, что это проблема дизайна из-за нелепого количества циклических зависимостей, происходящих здесь: Shape зависит от своих дочерних элементов, потому что некоторые из его виртуальных функций принимают дочерние элементы в качестве параметра, Polygon зависит от Shape (потому что он наследуется от него) и Circle (потому что у него есть функция detectCollision, принимающая параметр Circle&), а Circle зависит от Shape (потому что он наследуется от него) и Polygon (потому что у него есть функцияDetectCollision, принимающая параметр Polygon&). Самый простой выход, вероятно, просто отказаться от идеи помещать многоугольники и окружности в один контейнер последовательности и просто поместить их в два отдельных std::vectors. Тем не менее, я хотел бы знать, является ли мой первый дизайн просто невозможным по своей сути, возможным, но уродливым, или надежным, но неправильно реализованным. Обычно я предпочитаю находить подобные вещи самостоятельно, но после нескольких часов поиска и пробования различных решений я должен признать, что просто не понимаю этой проблемы.
(изменить: некоторые изменения кода, предложенные Дэйвом)
РЕДАКТИРОВАТЬ, ссылка на полный источник, если кому-то нужна дополнительная информация: https://github.com/KoenP/medieval-melee-combat-cpp
Я компилирую только файлы polygon.cpp, circle.cpp, vector.cpp и line.cpp; test.cpp в настоящее время не имеет значения. Не то, чтобы вектор и линия имели большое значение для этой ошибки, их некомпиляция (только компиляция многоугольника и круга) дает точно такую же ошибку. Точная команда, которую я использую, это «g++ -o test circle.cpp polygon.cpp line.cpp vector.cpp».