Выделите память объекта статически; инициализировать это динамически?

9

У меня есть объект, конструктор которого получает параметр. Если я знаю значение параметра во время компиляции, я могу построить объект статически:

static FOOOBJ foo(3);

(Я понимаю, что на самом деле это не делается статически, то есть компилятором, но фактически выполняется во время установки).

Но если я не знаю значения параметра во время компиляции, я все же хотел бы заранее выделить место для объекта, но сконструировать объект в этом пространстве во время выполнения. Можно ли это сделать без отдельного .initialize()метода?

JRobert
источник

Ответы:

3

Использование initialize()метода к классу противоречит принципу конструктора класса, то есть когда экземпляр класса был построен , он должен быть « готов к употреблению ».

Как следует из ответа Игнасио, синтаксис размещения C ++ намного лучше для ваших целей.

Однако в библиотеках Arduino синтаксис размещения не поддерживается "из коробки", поэтому вы должны реализовать его самостоятельно; не бойся, это довольно просто:

void* operator new(size_t size, void* ptr)
{
    return ptr;
}

Синтаксис размещения может быть сложным зверем в C ++, но для вашей конкретной цели его использование может быть довольно простым:

static char buffer[sizeof FOOOBJ];
static FOOOBJ* foo;

void setup() {
    ...
    foo = new (buffer) FOOOBJ(3);
    ...
}

Разница с вашим текущим кодом в том, что fooтеперь это указатель, поэтому любой вызов метода будет использовать ->вместо ..

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

static char buffer[sizeof FOOOBJ];
static FOOOBJ& foo = *((FOOOBJ*) buffer);

void setup() {
    ...
    new (buffer) FOOOBJ(3);
    ...
}

Проблема этого кода в том, что вы не можете знать, был ли fooон уже создан с реальным FOOOBJэкземпляром или нет; используя указатель, вы всегда можете проверить, есть он 0или нет.

Используя синтаксис размещения, вы должны знать, что вы не можете deleteиспользовать fooприведенный выше пример. Если вы хотите уничтожить foo(т.е. убедиться, что вызывается его деструктор), то вы должны явно вызвать деструктор:

foo->~FOOOBJ();
jfpoilpret
источник
1
Я не знал о синтаксисе, но это имеет смысл! Должен ли конструктор быть в курсе / разработан для этого? FOOOBJэто объект OneWire, использующий библиотеку Джима Стадта (v2.2). Я получаю сообщение error: no matching function for call to 'operator new(unsigned int, byte [14])'по newвызову. Похоже, что avr-g ++ может не понимать синтаксис.
JRobert
Да, вы правы, я использую Eclipse для своих проектов Arduino, и то, что я проверял, похоже, работало, за исключением того, что работала компиляция Eclipse C ++, а не avr-g ++! Я отредактировал свой ответ, чтобы показать простой обходной путь.
jfpoilpret
Что касается вашего вопроса о конструкторе, то здесь нет особых требований, но если конструктор сам выполняет динамическое размещение, то размещение нового не помешает этому.
jfpoilpret
Я также использую Eclipse - какой компилятор C ++ у вас настроен? Плюс, глядя на конструктор OneWire, он ничего не делает new, он просто инициализирует некоторые операции ввода-вывода .
JRobert
Я использую Eclipse с плагином eclipse.baeyens.it (который использует инструменты IDE Arduino, т.е. avr-g ++ + Arduino libs); но компиляция C ++ происходит на лету с Eclipse C ++ и использует avr-g ++ только при запуске сборки Arduino. Я не проверил последний шаг изначально.
jfpoilpret
4

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

FOOOBJ foo(0);

 ...

  FOOOBJ *f = new (foo) FOOOBJ(3);
Игнасио Васкес-Абрамс
источник
Я хотел бы предложить , чтобы заменить декларацию fooс char foo[sizeof FOOOBJ];тем чтобы FOOOBJконструктор не дозвонились для fooкоторых может быть реальной проблемой в зависимости от того, что делает конструктор.
jfpoilpret