Как решить жизненные проблемы при построении составного объекта с указателями на объекты - PullRequest
0 голосов
/ 12 марта 2020

Этот вопрос относится к этому другому. Я пытаюсь реализовать подход virtual method, предложенный в одном из ответов.

У меня есть абстрактный базовый класс, представляющий объект, описываемый функцией Level-Set

    class LevelSetObject
    {

    public:
        virtual double SDF(double x, double y, double z) const = 0;


        /** @brief Union of two LevelSetObjects */
        CompositeLevelSetObject operator+(LevelSetObject& other);


        virtual ~LevelSetObject() = default;

    };

Первая реализация, которая у меня была с std::function, работала более или менее как ожидалось. Разница в том, что я возвращаю CompositeLevelSetObject при сложении двух обобщенных c LevelSetObjects

       CompositeLevelSetObject LevelSetObject::operator+(LevelSetObject& other)  {
            CompositeFun* fun = [] (double sdf_value1, double sdf_value2) {
                return std::min(sdf_value1, sdf_value2);
            };

            auto first = std::unique_ptr<LevelSetObject>(new LevelSetObject(*this));
            auto second = std::unique_ptr<LevelSetObject>(new LevelSetObject(other));

            return { std::move(first), std::move(second), fun };
        }

A CompositeLevelSetObject, по крайней мере, на мой взгляд :), это объект, который принимает как входные указатели на два объекта, из которых он состоит, плюс указатель на функцию, которая вычисляет результирующую функцию расстояния (примитивные комбинации LevelSetObjects кодируются операциями между двумя отдельными функциями расстояния объектов ref ("Примитивные комбинации") )

    /** @brief This is the function that is called on the two single SDF function of the objects */
    using CompositeFun = double (double, double);


    /**
     * @brief A class representing a composite LevelSetObject coming out of operation between two other LevelSetObjects
     *
     * This class stores pointers to the two LevelSetObject and executes a given function on both of them
     */
    class CompositeLevelSetObject : public LevelSetObject
    {

    using LevelSetObjectPtr = std::unique_ptr<LevelSetObject>;

    private:
        LevelSetObjectPtr m_first;
        LevelSetObjectPtr m_second;
        CompositeFun* m_fun;


    public:
        CompositeLevelSetObject() = default;
        CompositeLevelSetObject(LevelSetObjectPtr first, LevelSetObjectPtr second, CompositeFun* m_fun);
        CompositeLevelSetObject(CompositeLevelSetObject&&);

        double SDF(double x, double y, double z) const override;

        // :: Operators ::
        CompositeLevelSetObject& operator=(CompositeLevelSetObject&&);
    };
}

    double CompositeLevelSetObject::SDF(double x, double y, double z) const {
        return m_fun(m_first->SDF(x, y, z), m_second->SDF(x, y, z));
    }




Это явно не работает, потому что в LevelSetObject::operator+ я создаю указатели, создающие экземпляр абстрактного класса.

allocating an object of abstract class type 'hgve::LevelSetObject'
            auto first = std::unique_ptr<LevelSetObject>(new LevelSetObject(*this));

Поэтому я попытался переключить метод LevelSetObject::SDF на не чистый, возвращая 0. Но явно не работает, потому что тогда все возвращает 0.

Я упускаю что-то в моей концептуальной организации структуры данных, вероятно, из-за моего динамического c языкового фона, но я не вижу этого , Любая помощь приветствуется.

Пример использования:

// Vector containing a list of LevelSetSpheres
static std::vector<LevelSetSphere> elements = {
    LevelSetSphere{radius, {radius, 0.25, 0.25}},
    LevelSetSphere{radius, {radius + interDistance, 0.25, 0.25}},
    LevelSetSphere{radius, {radius + 2*interDistance, 0.25, 0.25}}
};

int main(int argc, char* argv[]) {
    // Sum first two elements
    CompositeLevelSetObject soot = elements[0] + elements[1];
    for(auto el = std::next(elements.begin(), 2); el != elements.end(); ++el) {
        // Combine elements
        soot = soot + *el;
    }
}

LevelSetSphere является производным классом от LevelSetObject.

    class LevelSetSphere : public LevelSetObject
    {
    private:
        double m_R; /**< The radius of the sphere */
        SimpleVector m_C; /**< The center of the sphere */

    public:
        /** @brief Constructor
         *
         *  @param  radius      The radius of the sphere
         */
        LevelSetSphere(double radius, SimpleVector center);

        double SDF(double x, double y, double z) const override;
    };

1 Ответ

0 голосов
/ 12 марта 2020

Когда вы делаете new LevelSetObject, это создает новый объект, тип которого LevelSetObject. Кажется очевидным, но на это нужно указать.

Вам не нужен новый LevelSetObject, вам нужен новый LevelSetSphere (если входной объект представляет собой сферу).

Вы можете взять LevelSetObject и передать право собственности на unique_ptr без копирования, но вы должны быть осторожны с владельцем. Если вы хотите сделать это, лучше использовать unique_ptr с самого начала (вариант 3). Вы можете передать право собственности только в том случае, если объект был создан с помощью new. Локальные переменные, глобальные переменные и элементы векторов (как в вашем примере), среди прочего, не могут быть переданы в unique_ptr. Только объекты, созданные с new.

Вот три возможных решения:

  1. Добавить метод virtual std::unique_ptr<LevelSetObject> copy() const = 0; к LevelSetObject. (Реализуйте это в каждом производном классе, чтобы он делал копию)

  2. Заставьте объект CompositeLevelSet указывать на исходные LevelSetObject s вместо их копирования. Это означает, что вы не можете использовать составной объект после уничтожения исходных объектов. Это также означает, что промежуточные композиции нужно где-то сохранить. Не лучший вариант.

  3. Передайте право собственности на operator+, чтобы не нужно было копировать объекты. Например, определите unique_ptr<CompositeLevelSetObject> operator+(unique_ptr<LevelSetObject> left, unique_ptr<LevelSetObject> right). (Если это вас смущает, это может быть функция вместо оператора)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...