Why is it considerconsidered bad practice to use the new'new' keyword in arduinoArduino?
Is it required to delete variables before going to sleep? Is it required to delete variables before going to sleep?
On that question, @Delta_G posted this comment:
... Really on a microcontroller I would create the object in a smaller scope and try to do everything in my power to avoid having to use new
new
or any other form of dynamic allocation. .... etc.
That comment got 3three likes and when I google about dynamic allocation using arduinoArduino, everyone also tries to stay away from that. In summary from all the research I did, my conclusion is now Do not allocate memory unless you really really have to.
I am using visual stdiothe Visual Studio IDE to create my c++C++ libraries that I intend to use with arduinoArduino. On the arduinoArduino IDE I just reference those libraries and the code compiles great. Visual studioStudio is very powerful and it enables me to create really nice code, because I can test it on my computer before running it on Arduino. For example, I created this library:
// MyQueue.h
typedef struct QueueItem
{
void* item;
QueueItem* next;
QueueItem()
{
item = nullptr;
next = nullptr;
}
} QueueItem;
class Queue
{
public:
unsigned char count; /* numberNumber of items on queue */
QueueItem* first; /* pointsPoints to first item on the queue */
Queue() /* Constructor */
{
count = 0;
first = nullptr;
}
void enqueue(void* item) /* enqueEnqueue an object into the queue */
{
count++;
if (first == nullptr)
{
first = new QueueItem();
first->item = item;
// Log message because we are using the "new" keword. We need to make sure we dispose QueueItem later
#ifdef windows
std::cout << "Creating " << first << endl;
#endif // windows
}
else {
// findFind last item
QueueItem* current = first;
while (current->next != NULL)
{
current = current->next;
}
QueueItem* newItem = new QueueItem();
newItem->item = item;
// Log message because we are using the "new" kewordkeyword. We need to make sure we dispose QueueItem later
#ifdef windows
std::cout << "Creating " << newItem << endl;
#endif // windows
current->next = newItem;
}
}
void* dequeue()
{
if (count == 0)
return nullptr;
QueueItem* newFirst = first->next;
void* pointerToItem = first->item;
// logLog message we are deleting an object because we created it with the 'new' keyword
#ifdef windows
std::cout << "Deleting " << first << endl;
#endif // windows
delete first;
first = newFirst;
count--;
return pointerToItem;
}
void clear() /* emptyEmpty queue */
{
while (count > 0)
{
dequeue();
}
}
~Queue() /* DesctructorDestructor. Dispose everything */
{
clear();
}
};
Now on my arduinoArduino sketch, I can have the following code if I reference that header file.
typedef struct Foo
{
int id;
} Foo;
void someMethod()
{
Queue q;
// createCreate items
Foo a;
a.id = 1;
Foo b;
b.id = 2;
// enqueuEnqueue a,b and c
q.enqueue(&a);
q.enqueue(&b);
// dequeDeque
Foo * pointerTo_a = (Foo*)q.dequeue();
int x = pointerTo_a->id; // =1
Foo * pointerTo_b = (Foo*)q.dequeue();
int y = pointerTo_b->id; // =2
// errorError
Foo * test = (Foo*)q.dequeue();
// test == null pointer
}
So I guess my question is why: Why is everyone trying to stay away and avoid code like this?
I am using the NRF24L01 radio module to send messages to several arduinosArduinos. It is convenient to have a queue of messages to be sent. I would be able to code the same program without allocating memory and avoiding the new key wordnew
keyword. But that code will look ugly in my opinion.
In this quarantine I decided to learn c++C++ and that has changed the way I code arduinoArduino. The moment I learned c++C++ I stopped using the Arduino IDE. I been a backed developer for 12 years, and that is the reason why I learned c++C++ in a couple of months. Arduino is just a hobby for me. I am still very new to micro-controllersmicrocontrollers and I will like to understand why people stay away from the full power of c++C++ when it comes to micro controllersmicrocontrollers. I know I have only 2 kilobytes of ramRAM. I will not be allocating that much memory. I still want to take advantage of the c++C++ programming language by using the new
, delete
, poineters
and destructors`. I want to keep using visual studioVisual Studio to write powerful c++C++ libraries.
In c++C++ I write interfaces like this
// noteNote I use uint32_t instead of 'unsigned long' because an unsigned long is different size on windowsWindows than on arduinoArduino. Also I use an unsigned short instead of an int because an unsigned short is the same size on windowsWindows and arduinoArduino.
class IArduinoMethods
{
public:
// unsignedUnsigned long in arduinoArduino
virtual void delay(uint32_t delayInMilliseconds) = 0;
virtual void print(const char* text) = 0;
virtual uint32_t millis() = 0; // getGet elapsed time in milliseconds
};
andAnd I then implement the classes like this. For example, this is the class that I will use when testing my code on a windows computer:
// classClass to be run on windowsWindows.
class ArduinoMockWindows : public IArduinoMethods
{
public:
// Inherited via IArduinoMethods
virtual void delay(uint32_t delayInMilliseconds) override
{
// thisThis code will be different on arduinoArduino, and that is why I need this dependency
Sleep(delayInMilliseconds); // windows Windows
}
virtual uint32_t millis()
{
//clock_begin = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
// etc..
return someDuration;
}
};
Because windowsa Windows computer cannot send NRF24 radio messages, I can implement an interface (dependency) that will write to a file, for example instead of sending a real radio packet just for testing.
The caveat is that my libraries will require these dependencies. For my library to work, I will have to pass it an object of type IArduinoMethods
and INrfRadio
. If I am running my code on windowsWindows I will pass it a class that will implement those methods that can run on windows. Anyways the point is not to show how c++C++ works. I am just showing how I use pointers and allocate memory for a lot of things.
Because I allocated memory I was able to test my library on windowsWindows and on arduinoArduino for example. I may also create unit tests. I see so many benefits by allocating memory. If I am organized and remember to free the objects I no longer use, I can gain all this benefits. Why people do not code like this when it comes to arduinoArduino?
Now that I understand how heap fragmentation works, I know I have to be careful when using the new
keyword.
I hate when people do what they are told to do without understanding how things work. For example, the answer https://arduino.stackexchange.com/a/77078/51226 from . Why is the queue library in this question for starters?. There are going to be times when a ring buffer works better and other times when the newnew
keyword works better. Probably the ring buffer will work best for most cases.
- There is a hierarchy of nodes where a node have a child and a sibling. For example, node A can have child B and sibling C. Then child B can have another child, etc.
Reserve 500kb500 kB to be able to store nodes (I will be limited to n number of nodes)
Reserve 250kb250 kB for the queue of work that needs to be done.
Reserve 250kb250 kB for the queue of events.
Now if I have more work than events, this will work. I just have to program in my code that I never allocate more than 70 items. I will have a global variable that has that count of allocations. My code will be more flexible. I will not have to be stuck with strictly 20 events, 20 work and 30 nodes. If I have fewer nodes then I will be able to have more events. Anyways my point is that one solution is not better than the other. There are going to be scenarios when one solution is better. In conclusion just understand how heap fragmentation works and you will gain a lot of power by using the new keyword. Do not be a sheep and do what people tell you to do without understanding how things work.**Anyways my point is that one solution is not better than the other. There are going to be scenarios when one solution is better.
In conclusion, just understand how heap fragmentation works and you will gain a lot of power by using the new keyword. Do not be a sheep and do what people tell you to do without understanding how things work.**.
Thanks to @EdgarBonet, I ended up storing Nodes on the stack. Here is why:
asAs you can see every node is only 6 bytes. That is another reason why I did not care to much about allocating Nodes at the beginning. If I allocate this node on the heap I will be losing 2 more bytes (33%) for every allocation because on every allocation the size of the node has to be stored. As a result I created these 2two methods and a buffer:
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* bufferBuffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* methodMethod to store a node */
{
// findFind first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
return nullptr;
}
void freeNode(Node* nodeToFree) /* methodMethod to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
andAnd on my code I used to have things like:
Node * a = new Node();
a->id = 234423;
// ....
// .. etc
// ..
delete a;
Node * a = allocateNode({});
a->id = 234423;
// ....
// .. etc
// ..
freeNode(a);
andAnd my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer.
I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many. If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword. > Also in the case of the queue I will allocteallocate and delete objects very fast. Because the objects will not persist on memory for totoo long, and the chances of having heap fragmentation isare very low.
Why is it consider bad practice to use the new keyword in arduino?
Is it required to delete variables before going to sleep?
On that question @Delta_G posted this comment:
... Really on a microcontroller I would create the object in a smaller scope and try to do everything in my power to avoid having to use new or any other form of dynamic allocation. .... etc
That comment got 3 likes and when I google about dynamic allocation using arduino everyone also tries to stay away from that. In summary from all the research I did my conclusion is now Do not allocate memory unless you really really have to.
I am using visual stdio IDE to create my c++ libraries that I intend to use with arduino. On the arduino IDE I just reference those libraries and the code compiles great. Visual studio is very powerful and it enables me to create really nice code because I can test it on my computer before running it on Arduino. For example I created this library:
// MyQueue.h
typedef struct QueueItem
{
void* item;
QueueItem* next;
QueueItem()
{
item = nullptr;
next = nullptr;
}
} QueueItem;
class Queue
{
public:
unsigned char count; /* number of items on queue */
QueueItem* first; /* points to first item on the queue */
Queue() /* Constructor */
{
count = 0;
first = nullptr;
}
void enqueue(void* item) /* enque an object into the queue */
{
count++;
if (first == nullptr)
{
first = new QueueItem();
first->item = item;
// Log message because we are using the "new" keword. We need to make sure we dispose QueueItem later
#ifdef windows
std::cout << "Creating " << first << endl;
#endif // windows
}
else {
// find last item
QueueItem* current = first;
while (current->next != NULL)
{
current = current->next;
}
QueueItem* newItem = new QueueItem();
newItem->item = item;
// Log message because we are using the "new" keword. We need to make sure we dispose QueueItem later
#ifdef windows
std::cout << "Creating " << newItem << endl;
#endif // windows
current->next = newItem;
}
}
void* dequeue()
{
if (count == 0)
return nullptr;
QueueItem* newFirst = first->next;
void* pointerToItem = first->item;
// log message we are deleting an object because we created it with the 'new' keyword
#ifdef windows
std::cout << "Deleting " << first << endl;
#endif // windows
delete first;
first = newFirst;
count--;
return pointerToItem;
}
void clear() /* empty queue */
{
while (count > 0)
{
dequeue();
}
}
~Queue() /* Desctructor. Dispose everything */
{
clear();
}
};
Now on my arduino sketch I can have the following code if I reference that header file.
typedef struct Foo
{
int id;
} Foo;
void someMethod()
{
Queue q;
// create items
Foo a;
a.id = 1;
Foo b;
b.id = 2;
// enqueu a,b and c
q.enqueue(&a);
q.enqueue(&b);
// deque
Foo * pointerTo_a = (Foo*)q.dequeue();
int x = pointerTo_a->id; // =1
Foo * pointerTo_b = (Foo*)q.dequeue();
int y = pointerTo_b->id; // =2
// error
Foo * test = (Foo*)q.dequeue();
// test == null pointer
}
So I guess my question is why is everyone trying to stay away and avoid code like this?
I am using the NRF24L01 radio module to send messages to several arduinos. It is convenient to have a queue of messages to be sent. I would be able to code the same program without allocating memory and avoiding the new key word. But that code will look ugly in my opinion.
In this quarantine I decided to learn c++ and that has changed the way I code arduino. The moment I learned c++ I stopped using Arduino IDE. I been a backed developer for 12 years that is the reason why I learned c++ in a couple of months. Arduino is just a hobby for me. I am still very new to micro-controllers and I will like to understand why people stay away from the full power of c++ when it comes to micro controllers. I know I have only 2 kilobytes of ram. I will not be allocating that much memory. I still want to take advantage of the c++ programming language by using the new
, delete
, poineters
and destructors`. I want to keep using visual studio to write powerful c++ libraries.
In c++ I write interfaces like this
// note I use uint32_t instead of 'unsigned long' because an unsigned long is different size on windows than on arduino. Also I use an unsigned short instead of an int because an unsigned short is the same size on windows and arduino.
class IArduinoMethods
{
public:
// unsigned long in arduino
virtual void delay(uint32_t delayInMilliseconds) = 0;
virtual void print(const char* text) = 0;
virtual uint32_t millis() = 0; // get elapsed time in milliseconds
};
and I then implement the classes like this. For example this is the class that I will use when testing my code on a windows computer:
// class to be run on windows.
class ArduinoMockWindows : public IArduinoMethods
{
public:
// Inherited via IArduinoMethods
virtual void delay(uint32_t delayInMilliseconds) override
{
// this code will be different on arduino that is why I need this dependency
Sleep(delayInMilliseconds); // windows
}
virtual uint32_t millis()
{
//clock_begin = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
// etc..
return someDuration;
}
};
Because windows computer cannot send NRF24 radio messages I can implement an interface (dependency) that will write to a file for example instead of sending a real radio packet just for testing
The caveat is that my libraries will require these dependencies. For my library to work I will have to pass it an object of type IArduinoMethods
and INrfRadio
. If I am running my code on windows I will pass it a class that will implement those methods that can run on windows. Anyways the point is not to show how c++ works. I am just showing how I use pointers and allocate memory for a lot of things. Because I allocated memory I was able to test my library on windows and on arduino for example. I may also create unit tests. I see so many benefits by allocating memory. If I am organized and remember to free the objects I no longer use, I can gain all this benefits. Why people do not code like this when it comes to arduino?
Now that I understand how heap fragmentation works I know I have to be careful when using the new
keyword.
I hate when people do what they are told to do without understanding how things work. For example the answer https://arduino.stackexchange.com/a/77078/51226 from . Why is the queue library in this question for starters? There are going to be times when a ring buffer works better and other times when the new keyword works better. Probably the ring buffer will work best for most cases.
- There is a hierarchy of nodes where a node have a child and a sibling. For example node A can have child B and sibling C. Then child B can have another child etc.
Reserve 500kb to be able to store nodes (I will be limited to n number of nodes)
Reserve 250kb for the queue of work that needs to be done.
Reserve 250kb for the queue of events.
Now if I have more work than events this will work. I just have to program in my code that I never allocate more than 70 items. I will have a global variable that has that count of allocations. My code will be more flexible. I will not have to be stuck with strictly 20 events, 20 work and 30 nodes. If I have fewer nodes then I will be able to have more events. Anyways my point is that one solution is not better than the other. There are going to be scenarios when one solution is better. In conclusion just understand how heap fragmentation works and you will gain a lot of power by using the new keyword. Do not be a sheep and do what people tell you to do without understanding how things work..
Thanks to @EdgarBonet I ended up storing Nodes on the stack. Here is why:
as you can see every node is only 6 bytes. That is another reason why I did not care to much about allocating Nodes at the beginning. If I allocate this node on the heap I will be losing 2 more bytes (33%) for every allocation because on every allocation the size of the node has to be stored. As a result I created these 2 methods and a buffer:
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
return nullptr;
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
and on my code I used to have things like:
Node * a = new Node();
a->id = 234423;
// ....
// .. etc
// ..
delete a;
Node * a = allocateNode({});
a->id = 234423;
// ....
// .. etc
// ..
freeNode(a);
and my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer. I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many. If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword. > Also in the case of the queue I will allocte and delete objects very fast. Because the objects will not persist on memory for to long the chances of having heap fragmentation is very low.
Why is it considered bad practice to use the 'new' keyword in Arduino?
Is it required to delete variables before going to sleep?
On that question, @Delta_G posted this comment:
... Really on a microcontroller I would create the object in a smaller scope and try to do everything in my power to avoid having to use
new
or any other form of dynamic allocation. .... etc.
That comment got three likes and when I google about dynamic allocation using Arduino, everyone also tries to stay away from that. In summary from all the research I did, my conclusion is now Do not allocate memory unless you really really have to.
I am using the Visual Studio IDE to create my C++ libraries that I intend to use with Arduino. On the Arduino IDE I just reference those libraries and the code compiles great. Visual Studio is very powerful and it enables me to create really nice code, because I can test it on my computer before running it on Arduino. For example, I created this library:
// MyQueue.h
typedef struct QueueItem
{
void* item;
QueueItem* next;
QueueItem()
{
item = nullptr;
next = nullptr;
}
} QueueItem;
class Queue
{
public:
unsigned char count; /* Number of items on queue */
QueueItem* first; /* Points to first item on the queue */
Queue() /* Constructor */
{
count = 0;
first = nullptr;
}
void enqueue(void* item) /* Enqueue an object into the queue */
{
count++;
if (first == nullptr)
{
first = new QueueItem();
first->item = item;
// Log message because we are using the "new" keword. We need to make sure we dispose QueueItem later
#ifdef windows
std::cout << "Creating " << first << endl;
#endif // windows
}
else {
// Find last item
QueueItem* current = first;
while (current->next != NULL)
{
current = current->next;
}
QueueItem* newItem = new QueueItem();
newItem->item = item;
// Log message because we are using the "new" keyword. We need to make sure we dispose QueueItem later
#ifdef windows
std::cout << "Creating " << newItem << endl;
#endif // windows
current->next = newItem;
}
}
void* dequeue()
{
if (count == 0)
return nullptr;
QueueItem* newFirst = first->next;
void* pointerToItem = first->item;
// Log message we are deleting an object because we created it with the 'new' keyword
#ifdef windows
std::cout << "Deleting " << first << endl;
#endif // windows
delete first;
first = newFirst;
count--;
return pointerToItem;
}
void clear() /* Empty queue */
{
while (count > 0)
{
dequeue();
}
}
~Queue() /* Destructor. Dispose everything */
{
clear();
}
};
Now on my Arduino sketch, I can have the following code if I reference that header file.
typedef struct Foo
{
int id;
} Foo;
void someMethod()
{
Queue q;
// Create items
Foo a;
a.id = 1;
Foo b;
b.id = 2;
// Enqueue a,b and c
q.enqueue(&a);
q.enqueue(&b);
// Deque
Foo * pointerTo_a = (Foo*)q.dequeue();
int x = pointerTo_a->id; // =1
Foo * pointerTo_b = (Foo*)q.dequeue();
int y = pointerTo_b->id; // =2
// Error
Foo * test = (Foo*)q.dequeue();
// test == null pointer
}
So I guess my question is: Why is everyone trying to stay away and avoid code like this?
I am using the NRF24L01 radio module to send messages to several Arduinos. It is convenient to have a queue of messages to be sent. I would be able to code the same program without allocating memory and avoiding the new
keyword. But that code will look ugly in my opinion.
In this quarantine I decided to learn C++ and that has changed the way I code Arduino. The moment I learned C++ I stopped using the Arduino IDE. I been a backed developer for 12 years, and that is the reason why I learned C++ in a couple of months. Arduino is just a hobby for me. I am still very new to microcontrollers and I will like to understand why people stay away from the full power of C++ when it comes to microcontrollers. I know I have only 2 kilobytes of RAM. I will not be allocating that much memory. I still want to take advantage of the C++ programming language by using the new
, delete
, poineters
and destructors`. I want to keep using Visual Studio to write powerful C++ libraries.
In C++ I write interfaces like this
// Note I use uint32_t instead of 'unsigned long' because an unsigned long is different size on Windows than on Arduino. Also I use an unsigned short instead of an int because an unsigned short is the same size on Windows and Arduino.
class IArduinoMethods
{
public:
// Unsigned long in Arduino
virtual void delay(uint32_t delayInMilliseconds) = 0;
virtual void print(const char* text) = 0;
virtual uint32_t millis() = 0; // Get elapsed time in milliseconds
};
And I then implement the classes like this. For example, this is the class that I will use when testing my code on a windows computer:
// Class to be run on Windows.
class ArduinoMockWindows : public IArduinoMethods
{
public:
// Inherited via IArduinoMethods
virtual void delay(uint32_t delayInMilliseconds) override
{
// This code will be different on Arduino, and that is why I need this dependency
Sleep(delayInMilliseconds); // Windows
}
virtual uint32_t millis()
{
//clock_begin = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
// etc..
return someDuration;
}
};
Because a Windows computer cannot send NRF24 radio messages, I can implement an interface (dependency) that will write to a file, for example instead of sending a real radio packet just for testing.
The caveat is that my libraries will require these dependencies. For my library to work, I will have to pass it an object of type IArduinoMethods
and INrfRadio
. If I am running my code on Windows I will pass it a class that will implement those methods that can run on windows. Anyways the point is not to show how C++ works. I am just showing how I use pointers and allocate memory for a lot of things.
Because I allocated memory I was able to test my library on Windows and on Arduino for example. I may also create unit tests. I see so many benefits by allocating memory. If I am organized and remember to free the objects I no longer use, I can gain all this benefits. Why people do not code like this when it comes to Arduino?
Now that I understand how heap fragmentation works, I know I have to be careful when using the new
keyword.
I hate when people do what they are told to do without understanding how things work. For example, the answer https://arduino.stackexchange.com/a/77078/51226 from Why is the queue library in this question for starters?. There are going to be times when a ring buffer works better and other times when the new
keyword works better. Probably the ring buffer will work best for most cases.
- There is a hierarchy of nodes where a node have a child and a sibling. For example, node A can have child B and sibling C. Then child B can have another child, etc.
Reserve 500 kB to be able to store nodes (I will be limited to n number of nodes)
Reserve 250 kB for the queue of work that needs to be done.
Reserve 250 kB for the queue of events.
Now if I have more work than events, this will work. I just have to program in my code that I never allocate more than 70 items. I will have a global variable that has that count of allocations. My code will be more flexible. I will not have to be stuck with strictly 20 events, 20 work and 30 nodes. If I have fewer nodes then I will be able to have more events. **Anyways my point is that one solution is not better than the other. There are going to be scenarios when one solution is better.
In conclusion, just understand how heap fragmentation works and you will gain a lot of power by using the new keyword. Do not be a sheep and do what people tell you to do without understanding how things work.**.
Thanks to @EdgarBonet, I ended up storing Nodes on the stack. Here is why:
As you can see every node is only 6 bytes. That is another reason why I did not care to much about allocating Nodes at the beginning. If I allocate this node on the heap I will be losing 2 more bytes (33%) for every allocation because on every allocation the size of the node has to be stored. As a result I created these two methods and a buffer:
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* Buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* Method to store a node */
{
// Find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
return nullptr;
}
void freeNode(Node* nodeToFree) /* Method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
And on my code I used to have things like:
Node * a = new Node();
a->id = 234423;
// ....
// .. etc
// ..
delete a;
Node * a = allocateNode({});
a->id = 234423;
// ....
// .. etc
// ..
freeNode(a);
And my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer.
I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many. If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword. > Also in the case of the queue I will allocate and delete objects very fast. Because the objects will not persist on memory for too long, and the chances of having heap fragmentation are very low.
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
return nullptr;
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
and my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer. I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many. If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword. > Also in the case of the queue I will allocte and delete objects very fast. Because the objects will not persist on memory for to long the chances of having heap fragmentation is very low.
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
and my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer. I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many. If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword.
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
return nullptr;
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
and my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer. I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many. If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword. > Also in the case of the queue I will allocte and delete objects very fast. Because the objects will not persist on memory for to long the chances of having heap fragmentation is very low.
Edit 1
Thanks to @EdgarBonet I ended up storing Nodes on the stack. Here is why:
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
and my code works exactly the same without having to use the new
keyword. Now it is not that dangerous if I forgetthought it was going to delete objectsbe complicated to refactor the code and create a buffer. I made this modification because I wanted to be able to store more nodes on my code is still almost the same. By loosing that 33% I was not going to be able to create that many.If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword.
Edit
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0;
}
and my code works exactly the same without having to use the new
keyword. Now it is not that dangerous if I forget to delete objects and my code is still almost the same.
Edit 1
Thanks to @EdgarBonet I ended up storing Nodes on the stack. Here is why:
// For this to work a node can never have an id of 0 !!!
Node nodeBuffer[50]; /* buffer to store nodes on stack */
Node* allocateNode(Node nodeToAllocate) /* method to store a node */
{
// find first available spot where a node can be saved
for (char i = 0; i < 50; i++)
{
if (nodeBuffer[i].id == 0)
{
nodeBuffer[i] = nodeToAllocate;
return & nodeBuffer[i];
}
}
}
void freeNode(Node* nodeToFree) /* method to delete a node */
{
nodeToFree->id = 0; // If the id of a node is 0 this is my convention of knowing it is deleted.
}
and my code works exactly the same without having to use the new
keyword. I thought it was going to be complicated to refactor the code and create a buffer. I made this modification because I wanted to be able to store more nodes on my code. By loosing that 33% I was not going to be able to create that many.If I only allocate objects of the same size and I do not allocate that many it is perfectly fine to use the new
keyword.