Приемлемо ли новое без удаления?

Я знаю, что не рекомендуется использовать команду new для динамического создания данных, однако, если я никогда не буду использовать команду delete, приведет ли это к потере памяти пробелы/другие проблемы?

Справочная информация

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

В некоторых классах мне нужно создать список, и я не могу использовать тип с жестко запрограммированными значениями для заполнения массива.

Пример:

У меня есть класс LightSetup, содержащий список Pars (светодиодов):

class LightSetup
{
protected:
    Par* _pars[NR_OF_PARS];

Par — это реальная версия Arduino, для ее заполнения мне нужно:

LightSetup::LightSetup()
{
    for (int n = 0; n < NR_OF_PARS; n++)
    {
        _pars[n] = new Par(1 + 8 * n);
    }
}

В версии для ПК вместо нового Par я использую новый TestPar. А в заголовке я не могу это определить без такого указателя:

    Par _pars[NR_OF_PARS];

Иначе он не может содержать экземпляры TestPar.

, 👍0

Обсуждение

Вы можете избежать проблемы с новым удалением кучи, используя класс шаблона. Вот пример: https://github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/IOBuffer.hh#L29., @Mikael Patel

@MikaelPatel Спасибо за этот комментарий, это тоже звучит как хорошая идея ... забыл о шаблонах (последний раз использовал их 20 лет назад ;-))., @Michel Keijzers

Другим примером является класс шаблона GPIO, который компилирует цифровое чтение/запись в одну инструкцию: https://github.com/mikaelpatel/Arduino-GPIO/blob/master/src/Hardware/AVR/GPIO.h, @Mikael Patel

Должен сказать, что когда я впервые прочитал заголовок, я был вполне уверен в ответе: «Это никогда не приемлемо», но вы дали мне интересный сценарий для размышления. Это необычный случай, но действительный., @Kelly S. French

@MikaelPatel Спасибо ... мне нужно это проверить получше, выглядит очень полезно., @Michel Keijzers

@KellyS.French На самом деле я пришел к этой идее, потому что в моих профессиональных проектах мы заранее создавали экземпляры всех объектов, не из-за проблем с удалением/нехваткой памяти, а из-за производительности ( отсутствие создания новых объектов во время критических частей выполнения)., @Michel Keijzers

Вот еще один пример: Java позволяет вам предварительно выделить блок памяти (начальный размер кучи?) во время загрузки точно по той же причине, чтобы сократить выделение памяти во время выполнения. На самом деле это может стать хорошим общим вопросом о StackOverflow для общего случая, и я бы посоветовал вам написать об этом., @Kelly S. French

@KellyS.French Спасибо за это, хотя я не могу использовать Java для Arduino (по крайней мере, в Arduino IDE)., @Michel Keijzers


2 ответа


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

2

Конечно. Использование new или malloc для первоначальной настройки на самом деле ничем не отличается от статического распределения. Нет ничего плохого в использовании malloc или new, только в многократном использовании с free или delete.< /п>

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

,

Спасибо за подтверждение. Единственный недостаток, который я вижу, это то, что используемая память не рассчитывается по глобальным переменным в Arduino IDE., @Michel Keijzers

Ага. Отсюда и подталкивание следить за тем, чтобы вы не выделяли слишком много :), @Majenko

Возможно, в целях отладки я покажу объем зарезервированной памяти после новых операторов., @Michel Keijzers


3

В деструкторе вам понадобится

LightSetup::~LightSetup()
{
    for (int n = 0; n < NR_OF_PARS; n++)
    {
        delete _pars[n];
    }
}

и каждый раз, когда вы заменяете _pars, вам нужно удалить старый. Это можно сделать автоматически с помощью unique_ptr.

Однако для вашей цели это звучит так, будто #if лучше подходит для того, чтобы во время компиляции различать, какой тип вы фактически храните внутри этого массива.

#if ON_ARDUINO
    Par _pars[NR_OF_PARS];
#else
    TestPar _pars[NR_OF_PARS];
#endif
,

Что ж, деструктор никогда не будет вызываться, поскольку в моем приложении Arduino цикл будет выполняться вечно, так что это не проблема. Но я проголосовал за последнюю часть, возможно, это даже лучше, чем новая команда (хотя она немного загромождает код). Однако, если это единственное изменение, все проще. Спасибо, @Michel Keijzers

Я согласен с @ratchet по поводу #if, поскольку описанный вами случай касается не динамической типизации, а скорее разницы в платформах. Если вам не нужен один и тот же двоичный файл для запуска на обеих платформах (если это вообще возможно), вам лучше подойдет условная компиляция. Это также устраняет необходимость возиться с деструктором., @Kelly S. French