2009-06-23 21 views
6

Sto lavorando in un ambiente embedded (Arduino/AVR ATMega328) e voglio implementare il pattern Metodo di fabbrica in C++. Tuttavia, il compilatore che sto utilizzando (avr-gcc) non supporta la parola chiave new. C'è un modo per implementare questo modello senza utilizzare new?Posso implementare il pattern Metodo di fabbrica in C++ senza usare nuovo?

+2

Come allocare memoria? D: – GManNickG

+2

A parte questo, supporta il posizionamento nuovo? Il meglio che riesco a pensare è quello di allocare staticamente un pezzo di memoria, quindi puoi posizionare qualcosa di nuovo lì. – GManNickG

+2

La memoria è allocata nello stack (inclusa la costruzione di oggetti basata su stack) o con malloc() - che non supporta la costruzione di oggetti. Il posizionamento nuovo non è supportato ... –

risposta

7

Poiché il compilatore AVR è basato sul compilatore gcc, è molto probabile che supporti la nuova parola chiave. Qual è esattamente l'errore che stai ricevendo. Sto indovinando che si tratta di un errore di link/compilatore sulla falsariga di una funzione indefinita, vale a dire, operatore nuovo. C'è una differenza tra il nuovo operatore e l'operatore nuovo, il primo è usato per creare oggetti e il secondo è usato per allocare memoria per gli oggetti. Il nuovo operatore chiama l'operatore new per il tipo di oggetto che viene creato, quindi inizializza la tabella v dell'oggetto e chiama i costruttori dell'oggetto. Reading this FAQ dice che l'operatore new non è definito nelle librerie standard. Questo è facile da risolvere, basta definire un:

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

e sarà necessario definire un eliminare così:

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

L'unica cosa da aggiungere è la gestione della memoria, l'allocazione e la liberazione di blocchi di memoria. Questo può essere fatto banalmente, facendo attenzione a non clobare alcuna memoria allocata esistente (il codice, dati statici/globali, lo stack).Dovresti avere due simboli definiti: uno per l'inizio della memoria libera e uno per la fine della memoria libera. È possibile allocare e liberare dinamicamente qualsiasi blocco di memoria in questa regione. Dovrai gestire questa memoria da solo.

+0

+1 - Ho intenzione di combinare questo con la risposta di Tal e provarlo. –

0

Puoi fare malloc? Se è così, puoi mallocare il tuo oggetto in questo modo.

Inoltre, qual è la natura dei tuoi oggetti che desideri creare dalla fabbrica?

  • Sono imutable?
  • La fabbrica ha l'unico scopo di produrre un insieme limitato di oggetti che possono essere conosciuti al momento della compilazione?

Se la risposta è sì a entrambe le domande, è possibile allocare staticamente la memoria per il set di oggetti immutabili e lasciare che il metodo factory restituisca i puntatori all'oggetto appropriato.

Questo non funzionerà se la risposta non è in nessuna delle due domande. Anche con questo approccio hai il problema di avere sempre quella memoria allocata.

3

Se non è possibile istanziare una classe in fase di esecuzione, suppongo che ciò non sia possibile. Tutto quello che puoi fare è pre-allocare alcuni oggetti in fase di compilazione, creare riferimenti a loro e restituirli quando necessario.

1

E qualcosa di simile?

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

MODIFICA: dimenticato di menzionare, presuppone che MyClass abbia un operatore di assegnazione.

EDIT2: Un'altra cosa che ho dimenticato - sì, c'è un gotcha (è C++, ci sono sempre trucchi). Dovrai chiamare il manuale manualmente per l'oggetto, dal momento che non puoi usare gratuitamente.

+0

Almeno funziona in VC++ 7. – sharptooth

+1

Mia madre mi ha sempre avvertito dell'uso di malloc() per creare un'istanza di un oggetto C++. Anche se stai chiamando il costruttore e copi i suoi dati nella memoria allocata, ci sono dei trucchi con questo approccio? –

+1

Sì, ci sono trucchi ovvi: il blocco di memoria non è inizializzato e quindi l'operatore di assegnazione dovrebbe reagire di conseguenza - se vede un membro puntatore "inizializzato" non dovrebbe cercare di liberarlo perché contiene spazzatura e penzola. Suppongo che sia meglio chiamare semplicemente calloc() per ottenere blocchi di memoria inizializzati a zero per evitarlo. – sharptooth

0

Se si utilizza factory vuol dire che si desidera un comportamento di collegamento dinamico che indica che si dispone di alcune funzioni virtuali. Sebbene, potrebbe essere possibile allocare la memoria per l'oggetto usando malloc(), il vtable della classe non verrà configurato correttamente e quindi la chiamata alle funzioni virtuali andrà in crash. Non vedo alcun modo per farlo quando è richiesto il binding dinamico.

+0

Ciò potrebbe essere risolto: allocare un oggetto nello stack e richiamare memcpy affinché il puntatore vtable lo copi sull'oggetto appena assegnato all'heap. – sharptooth

+0

Quindi stai dicendo che la risposta di Tal (http://stackoverflow.com/questions/1031301/can-i-implement-the-factory-method-pattern-in-c-without-using-new/1031375#1031375) ha vinto funziona? –

+0

Non l'ho provato. Ma secondo me non funzionerà. Tuttavia, la soluzione alternativa suggerita da sharptooth potrebbe funzionare. – Naveen

0

Un modo in cui ho risolto questo problema in un sistema embedded con rigidi standard di codifica (in cui non era consentito utilizzare "nuovo" o "cancella") era creare una matrice statica dell'oggetto desiderato. Quindi utilizzare i puntatori statici sugli oggetti già allocati, archiviando questi valori restituiti (utilizzando variabili statiche e/o variabili membro) per l'esecuzione successiva dei vari oggetti.

// 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; 
    } 
} 

Si prega di essere avvisati. Questo è tutto dalla memoria in quanto non ho scritto alcun C++ in oltre 8 mesi.

Nota anche: questo ha un grave svantaggio in quanto si sta allocando un mucchio di RAM in fase di compilazione.

+0

È possibile definire un operatore MyObject :: new (size_t) che fa tutto quanto sopra, cioè utilizza un array preassegnato, mantenendo la sintassi MyObject (args) al posto di qualcosa di non standard. In questo modo, puoi costruire e destrutturare correttamente gli oggetti. – Skizz

+3

In un sistema embedded, considero l'assegnazione di un gruppo di RAN al momento della compilazione una buona cosa, non un inconveniente –

3

L'immagine grande del metodo Factory è la creazione di un oggetto, che significa consumo di memoria heap. Su un sistema embedded, sei vincolato dalla RAM e devi prendere tutte le decisioni di progettazione tenendo conto dei limiti di memoria. L'ATmega328 ha solo 2 KB di RAM. Ti consiglio di non utilizzare la memoria allocata dinamicamente in uno spazio così ristretto.

Senza conoscere il problema in modo più dettagliato, consiglierei di dichiarare staticamente una manciata di istanze della classe e riutilizzare quelle istanze in qualche modo. Ciò significa che devi sapere quando e perché i tuoi oggetti sono creati e - SOLO QUANTO IMPORTANTE - quando e perché finiscono; allora hai bisogno di capire quanti hai bisogno di avere attivo in una volta e quanti è possibile avere attivo in una volta.

!! Dean

+0

Tutto perché ha 2 KB o RAM non significa che non è possibile eseguire l'allocazione della memoria dinamica. Non puoi assegnare tanto. È possibile ottenere 500 oggetti a due byte (500 * (2 per oggetto + 2 per informazioni di allocazione) = 2000). – Skizz

+0

Skizz, le prime due frasi sono corrette, ma la terza non è ragionevole. La RAM da 2 KB (2048 byte) contiene sia lo stack C che l'heap; quindi avrebbe avuto solo 48 byte per il suo stack C (irragionevole). Un programma che ha bisogno di un metodo di fabbrica non è semplice, quindi il suo stack di chiamate C sta andando a variare in profondità e potrebbe potenzialmente sovrascrivere gli oggetti nell'heap. L'allocazione dinamica della memoria è possibile, ma non è sicura in un così piccolo pool di memoria. L'allocazione statica della memoria sarà più facile da gestire e più propensa a produrre un programma correttamente funzionante. – dwhall