Доступ к функции в объекте через `->` приводит к сбою (сбросу) Arduino

c++ reset pointer object-oriented vector

У меня есть несколько классов, которые вызывают друг друга, работающих на Arduino Mega. Я использую библиотеку ArduinoSTL, чтобы получить библиотеку std, в первую очередь std::vector.

Основная проблема заключается в следующем: мой объект Manager, созданный в глобальном пространстве Arduino, вызывается в цикле(). Объект Manager в одной из своих функций порождает производный объект Derived derived (конструктор также принимает pin int и значение задержки, но пока просто упрощает) и вызывает в нем функцию derived.startExecution(). Все довольны. Затем я сохраняю указатель на объект в std::vector<base*> MyVector через MyVector.push_back(&derived). Все довольны. Пока я не покидаю функцию, я могу вынуть указатель из векторного контейнера и вызвать другую функцию в объекте. Например: MyVector.back()->continueExecution() и все отлично. Теперь проблема. Если я скопирую строку MyVector.back()->continueExecution() в другую функцию, Arduino перезапустится. Я теряю рассудок. Я знаю, что проблема не в указателе, а в вызове функции с помощью указателя. Используя println & delay(500), в новой функции внутри Менеджера я могу сделать следующее без проблем (без перезагрузки):

  • извлечение указателя из вектора. Все хорошо.
  • назначьте указатель из вектора временному типу указателя, чтобы иметь возможность ссылаться на этот указатель. Все хорошо
  • вызов функции в объекте вызывает мгновенный аппаратный сброс Arduino (ни одна строка не будет выполнена в вызываемой функции).

Все это приводит меня к выводу, что вызов функции в объекте вызывает проблему. Я переписал упрощенную версию кода ниже, и он хорошо работает на этом сайте (см.: https://godbolt.org/z/G3x556), но на Arduino он выходит из строя.

Что я могу сделать, чтобы понять, почему объект завершает работу программы? как я могу проверить, что объект в порядке, прежде чем вызывать функцию? Почему вызов функции в объекте, который должен быть живым, приводит к сбою системы? почему вызов работает в функции, которая сохранила его в векторе, но не в другой функции? Как я могу ответить на эти вопросы? Я достиг предела идей. Спасибо, что уделили мне время!

Мой код упрощен:

#include <iostream>
//#include <ArduinoSTL.h> //закомментировано для работы на сайте
#include <vector>

//базовый класс используется для того, чтобы иметь возможность помещать все в std::vector
//в ArduinoSTL lib.
class Base
{
    public:
        virtual void continueExecution() 
        { std::cout << "Base" << std::endl; }

        // полиморфный класс ДОЛЖЕН иметь виртуальный деструктор
        virtual ~Base(){};
};

//многие из этих классов будут существовать в один прекрасный день, если я продолжу этот
class Derived : public Base
{
    public:
        Derived()
        {
            std::cout<< "Derived constructor" << std::endl;
        }

        void startExecution()
        {
            std::cout << "Derived object, start function"<< std::endl;
            //начало работы
        }

        void continueExecution()
        { 
            //продолжить по истечении времени
            std::cout << "Derived continueExectution " << std::endl; 
        }
};

class Manager
{
    public:
        void beginExecutingDerivedObject()
        {
    
            //производный объект
            Derived derivedObj;

            derivedObj.startExecution();

            myVector.push_back(&derivedObj);

            //если я запущу строку, которая приведет к сбою кода ниже этого
            //комментарий, все в порядке. Но это не то, что нужно.
            myVector.back()->continueExecution();
        }

        void finishExecutionOfDerivedObject()
        {
            //СБОЙ КОДА ЗДЕСЬ. В тот момент, когда функция вызывается, происходит сброс
            //происходит.
            myVector.back()->continueExecution();

            //любой код здесь может появиться на консоли, но это всего лишь
            //дамп памяти. К этому моменту Arduino уже развел руками.
        }            

    private:
        //создать вектор
        std::vector<Base*> myVector;   
};


//main-это на самом деле arduino loop()
int main()
{
    //этот класс менеджера находится в глобальном пространстве sketch
    Manager manager;

    //это вызывается вызовом функции в цикле()
    manager.beginExecutingDerivedObject();

    //это также вызывается вызовом функции в цикле()
    manager.finishExecutionOfDerivedObject();
}

, 👍0


1 ответ


Лучший ответ:

4

Derived derivedObj; - локальная переменная. Он удаляется при выходе из функции. Вы сохранили указатель на него, но теперь, когда этот объект исчез, ваш указатель не указывает ни на что интересное, так что делать с ним что - либо обречено на провал.

Вместо этого вам нужно создать новый объект в куче, который уже будет указателем:

Derived *derivedObj = new Derived();
derivedObj->startExecution();
myVector.push_back(derivedObj);

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

,

Обратите внимание, что std::vector<Base*> MyVector; - это вектор указателей. Вы _could_ храните значения в векторе как std::vector<Base> MyVector;, но тогда вы не можете хранить экземпляры производных классов., @PMF

@Mike В вашем случае это немного сложно, потому что вы должны делать это, когда удаляете элементы или очищаете список MyVector. (Повторите его с помощью цикла "for" и удалите все элементы, прежде чем очистить вектор). Использование STL (и некоторых других функций, которые вы используете выше) - довольно продвинутый материал для работы на Arduino, просто потому, что его память очень ограничена. Вероятно, вам следует сначала ознакомиться с некоторыми учебниками по C ++ и запустить несколько тестовых программ на ПК (где их гораздо легче отлаживать)., @PMF

Интеллектуальный указатель C ++ 11 shared_ptr упрощает его, но не знает, поддерживает ли их ArduinoSTL., @Majenko

@Majenko Ты РОК!!! Я совсем не знаю C ++, и его так трудно выучить на лету. Огромное спасибо за ваш ответ. Могу ли я испытать свою удачу и, пожалуйста, попросить пример того, как использовать слово delete? Я совершенно новичок в C ++ и очень ценю это. Не могли бы вы в своем примере предположить, что используется C ++ 99 (если мне посчастливится его получить)?, @Mike

Base *ob = MyVector[0]; MyVector.erase(0); delete ob;, @Majenko

@Majenko Я очень новичок в c ++, и у меня есть окончательное продолжение вашего примера удалить. В вашем примере сначала вы копируете указатель из вектора (так же, как: Base *myObj = MyVector.back();. Затем вы стираете элемент в векторном указателе (то же самое, что MyVector.pop_back();). Наконец, вы освобождаете память с помощью указателя. Я прав? Кроме того, когда мы "удаляем myObj", мы не удаляем указатель базового типа? разве мы не должны удалять фактический объект, или это действительно удаляет указатель И объект? Извините, что докучаю вам. Я очень ценю вашу помощь!, @Mike

удалить - это то же самое, что "новый" - это то же самое, что "свободный" - это "malloc". Вы даете ему указатель (поскольку new предоставляет указатель), и он освобождает объект (вызывая деструктор), на который указывает указатель. С новым вы никогда не "имеете" объект. У вас всегда есть только указатель (с соответствующим типом)., @Majenko