Вместо реинициализации объектов - PullRequest
5 голосов
/ 29 мая 2019

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

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

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

Единственный выход, о котором я думаю, - написать шаблон «обновить» (это всего лишь черновик, может содержать ошибки и, возможно, не завершен):

template<typename T, typename .. Args>
void renew(T & obj, Args&&... args) {
  obj.~T();
  new(&obj) T(std::forward<Args>(args)...); 
}

, который можно использовать для повторной инициализации нединамически распределенных переменных. Так например

A a{17};
... //Do something with a
renew(a, 14);
...//work with the new a, no need to reallocate memory

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

Вопрос: это разумный способ сделать это? Или есть лучшая альтернатива?

Здесь очень похожий вопрос Вызов конструктора для повторной инициализации объекта . Я спрашиваю о том же самом, но конкретно в контексте встроенного программирования, где я не могу делать вещи обычным способом с динамическим размещением.

Ответы [ 3 ]

3 голосов
/ 29 мая 2019

Динамическое распределение памяти и конструкторы и деструкторы полностью не связаны.

Если вы используете new или delete (как в вашей функции обновления), вы используете динамическое выделение памяти.

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

Вероятно, функция 'renew' должна быть реализована в operator = класса, а не как внешняя функция, поэтому:

A a{17}
...
a = 14;
...

Пример того, что вы должны сделать:

class TwoInts
{
private:
    int int1;
    int int2;
public:
    TwoInts(int a = 6, int b = 7): int1(a), int2(b) {}
    TwoInts(const TwoInts& other): int1(other.int1), int2(other.int2) {}

    TwoInts& operator=(TwoInts& other)
    {
        a = other.a;
        b = other.b;
    }
};

TwoInts i(16);
//do stuff
i = TwoInts(68, 14);
//do stuff

Приведенный выше код не выполняет никакого выделения памяти.

1 голос
/ 29 мая 2019

Единственная причина, по которой я вижу, что вы хотели бы предложить предложенную вами конструкцию, - это то, где новый класс будет другого производного типа. Но в этом случае вы должны быть очень осторожны, чтобы использовать точно такой же объем памяти, чтобы не переопределять следующие переменные. Я знаю один пример, где это используется в mbed . Он использовал, чтобы перепечатывать их функции обратного вызова, искать «новый», и вы будете различные примеры.

Мне интересно, рассматривали ли вы возможность использования в своем классе функции очистки? По моему честному мнению, насколько я вижу сейчас, это было бы самым простым решением.

Альтернативным решением будет написать менеджер памяти с фиксированным размером буфера. Из этого буфера вы можете «динамически» распределять память. Я не думаю, что это решение, которое вы ищете, так как оно более полезно, когда вам часто приходится выделять и освобождать несколько объектов.

0 голосов
/ 29 мая 2019

Я не могу использовать динамическое выделение памяти

Тогда ключевые слова new и delete не могут существовать в вашем источнике.Также убедитесь, что вы удалили сегмент .heap из вашей карты памяти.

Точно так же вам не нужен / не нужен RAII для любого класса, который является драйвером.В терминах модного слова, любой такой класс должен быть «синглтоном».Или, скорее, класс, который допускает n количество статических экземпляров, где n - это количество имеющихся аппаратных устройств этого типа.

Что касается решения дляВаша проблема, это просто:

public:
  void construct() { ... }

  Foo()
  : /* init internal stuff here if needed */
  {
    /* init internal stuff here if needed */    
    construct();
  }

Где конструктор гарантирует, что все имеет значения по умолчанию, и construct записывается так, что он либо работает с неинициализированными внутренними объектами (ноль и т. д.), либо весли внутренние устройства установлены, высвободите ресурсы.

Но вам даже не нужно ничего «освобождать», поскольку это встроенная система.Если вам нужно аккуратно прекратить текущую передачу данных, запись в NVM и т. Д., Этот код не должен находиться в деструкторе.Потому что вам не нужен деструктор (равно как и остальные правила из трех ), поскольку у вас должен быть только один экземпляр класса.

...