2009-06-23 2 views
6

Я работаю во встроенной среде (Arduino/AVR ATMega328) и хочу реализовать шаблон Factory Method в C++. Однако компилятор, который я использую (avr-gcc), не поддерживает ключевое слово new. Есть ли способ реализовать этот шаблон без использования new?Могу ли я реализовать шаблон Factory Method в C++ без использования нового?

+2

Как вы выделяете память? D: – GManNickG

+2

Кроме того, поддерживает ли он размещение нового? Лучшее, что я могу придумать, - статически выделять кусок памяти, тогда вы можете разместить там что-то новое. – GManNickG

+2

Память либо выделена в стеке (включая построение объекта на основе стека), либо с помощью malloc() - который не поддерживает конструкцию объекта. Размещение нового не поддерживается ни ... –

ответ

7

Поскольку компилятор AVR основан на компиляторе gcc, он, скорее всего, поддержит новое ключевое слово. Какова именно ошибка, которую вы получаете. Я предполагаю, что это ошибка ссылки/компилятора в строках неопределенной функции, а именно оператор new. Существует различие между новым оператором и оператором new, первый используется для создания объектов, а последний используется для выделения памяти для объектов. Новый оператор вызывает оператор new для типа создаваемого объекта, затем инициализирует v-таблицу объекта и вызывает конструкторы объекта. Reading this FAQ говорится, что оператор new не определен в стандартных библиотеках. Это легко исправить, просто определить одно:

void *operator new (size_t size) 
{ 
    return some allocated memory big enough to hold size bytes 
} 

и вам необходимо определить удаление, а также:

void operator delete (void *memory) 
{ 
    free the memory 
} 

Единственное, что нужно добавить, управление памятью, выделение и освобождение блоков памяти. Это можно сделать тривиально, стараясь не склеивать любую существующую выделенную память (код, статические/глобальные данные, стек).У вас должно быть два символа - один для начала свободной памяти и один для конца свободной памяти. Вы можете динамически выделять и освобождать любой фрагмент памяти в этом регионе. Вам нужно будет управлять этой памятью самостоятельно.

+0

+1 - Я собираюсь объединить это с ответом Тэла и дать ему уйти. –

0

Можете ли вы сделать malloc? Если это так, вы можете malloc ваш объект таким образом.

Также какова природа ваших объектов, которые вы хотите создать с фабрики?

  • Являются ли они неотразимыми?
  • Является ли завод только предназначенным для создания ограниченного набора объектов, которые могут быть известны во время компиляции?

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

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

3

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

1

Как насчет этого?

MyClass *objp = (MyClass*)malloc(sizeof(MyClass)); 
*objp = MyClass(); // or any other c'tor 

EDIT: Забыл упомянуть, предполагается, что MyClass имеет оператор присваивания.

EDIT2: Еще одна вещь, которую я забыл - да, есть gotcha (это C++, всегда есть gotchas). Вам нужно будет вызвать дентер вручную для объекта, поскольку вы не можете использовать его бесплатно.

+0

По крайней мере, он работает в VC++ 7. – sharptooth

+1

Моя мать всегда предупреждала меня об использовании malloc() для создания экземпляра объекта C++. Даже если вы вызываете конструктор и копируете его данные в выделенную память, есть ли какие-либо проблемы с этим подходом? –

+1

Да, есть очевидные ошибки: блок памяти не инициализирован, поэтому оператор присваивания должен реагировать соответственно - если он видит «инициализированный» указатель, он не должен пытаться освободить его, потому что он содержит мусор и висит. Я полагаю, что лучше всего вызвать calloc(), чтобы получить нулевой инициализированный блок памяти, чтобы этого избежать. – sharptooth

0

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

+0

Это можно обойти - выделить объект в стеке и вызвать memcpy для указателя vtable, чтобы скопировать его на выделенный объект, выделенный в кучу. – sharptooth

+0

Итак, вы говорите, что ответ Тэла (http://stackoverflow.com/questions/1031301/can-i-implement-the-factory-method-pattern-in-c-without-using-new/1031375#1031375) выиграл Работа? –

+0

Я не пробовал. Но, по моему мнению, это не сработает. Однако обходной путь, предложенный sharptooth, может работать. – Naveen

0

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

// Class File --------------------------------------------------- 
class MyObject { 
    public: 
     MyObject* getObject(); 

    private: 
     const int MAX_POSSIBLE_COUNT_OF_OBJECTS = 10; 
     static MyObject allocatedObjects[MAX_POSSIBLE_COUNT_OF_OBJECTS]; 

     static allocatedObjectIndex = 0; 
}; 

// Implementation File ------------------------------------------ 

// Instantiate a static array of your objects. 
static MyObject::allocatedObject[MAX_POSSIBLE_COUNT_OF_OBJECTS]; 

// Your method to return already created objects. 
MyObject* MyObject::getObject() { 

    if (allocatedObjectIndex < (MAX_POSSIBLE_COUNT_OF_OBJECTS - 1)) { 
     return allocatedObjects[allocatedObjectIndex++]; 
    } else { 
     // Log error if possible 
     return NULL; 
    } 
} 

Пожалуйста, предоставьте предупреждение. Это все из памяти, поскольку я не написал никаких C++ более чем за 8 месяцев.

Также обратите внимание: это имеет серьезный недостаток в том, что вы выделяете кучу ОЗУ во время компиляции.

+0

Вы можете определить оператор MyObject :: new (size_t), который делает все вышеперечисленное, т. Е. Использует предварительно выделенный массив, сохраняя при этом знакомый новый синтаксис MyObject (args) вместо чего-то нестандартного. Таким образом, вы можете правильно построить и уничтожить объекты. – Skizz

+3

Во встроенной системе я рассматриваю выделение группы RAN во время компиляции Good Thing, а не недостаток. –

3

Общая картина метода Factory - это создание объекта, что означает потребление памяти кучи. На встроенной системе вы ограничены оперативной памятью и должны принимать во внимание все ваши проектные решения с учетом ваших ограничений памяти. ATmega328 имеет только 2 КБ оперативной памяти. Я бы рекомендовал не использовать динамически выделенную память в таком ограниченном пространстве.

Не зная вашей проблемы более подробно, я бы рекомендовал статически объявить несколько экземпляров класса и повторно использовать эти экземпляры в некотором роде. Это означает, что вам нужно знать, когда и почему создаются ваши объекты, и - ТОЛЬКО КАК ВАЖНО - когда и почему они заканчиваются; то вам нужно выяснить, сколько вам нужно иметь активную за один раз и сколько из них можно активировать за один раз.

!! Dean

+0

Все, потому что имеет 2 КБ или ОЗУ, не означает, что вы не можете выполнять динамическое распределение памяти. Вы просто не можете выделить столько. Вы можете получить 500 двухбайтовых объектов (500 * (2 для объекта + 2 для информации о размещении) = 2000). – Skizz

+0

Skizz, ваши первые два предложения верны, но третий не является разумным. ОЗУ объемом 2 КБ (2048 байт) содержит как стек C, так и кучу; поэтому у него будет всего 48 байт для его стека C (необоснованно). Программа, которая нуждается в Factory Method, не является простой, поэтому его стек вызовов C будет меняться по глубине и может потенциально перезаписать объекты в куче. Динамическое распределение памяти возможно, но не безопасно в таком небольшом пуле памяти. Статическое распределение памяти будет легче управлять и, скорее всего, даст правильную программу. – dwhall

Смежные вопросы