I know it is not a good practice to use the new
command to dynamically create data, however, if I would never use the delete
, would it result in memory gaps/other problems?
Background
The reason I need to create instances dynamically, is that I want to test my application first on a PC. And I want for several classes to have more properties for the PC version than for the Arduino version (for example, to store names, or information for the PC GUI to display). The PC version classes will be inherited from the Arduino (real) classes, but with added properties.
In some classes I need to build up a list, and I cannot use a type with hardcoded values to fill an array.
Example:
I have a LightSetup class that has a list of Pars (which are LED lights):
class LightSetup
{
protected:
Par* _pars[NR_OF_PARS];
Par is the Arduino/real version, to fill in these I need:
LightSetup::LightSetup()
{
for (int n = 0; n < NR_OF_PARS; n++)
{
_pars[n] = new Par(1 + 8 * n);
}
}
In the PC version, instead of new Par, I use new TestPar. And in the header I cannot define it without a pointer like this:
Par _pars[NR_OF_PARS];
Otherwise it cannot contain TestPar instances.
-
1You could avoid the new-delete-heap-issue by using a template class. Here is an example: github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/…Mikael Patel– Mikael Patel2019年05月08日 10:54:51 +00:00Commented May 8, 2019 at 10:54
-
@MikaelPatel Thanks for this comment, it also sounds like a good idea ... forgot about templates (having used it 20 years ago for the last time ;-) ).Michel Keijzers– Michel Keijzers2019年05月08日 12:14:42 +00:00Commented May 8, 2019 at 12:14
-
1Another example is this GPIO template class that compile digitalRead/Write to a single instruction: github.com/mikaelpatel/Arduino-GPIO/blob/master/src/Hardware/…Mikael Patel– Mikael Patel2019年05月08日 17:00:44 +00:00Commented May 8, 2019 at 17:00
-
1I have to say, when I first read the title I was pretty set on the answer being "It is never acceptable" but you have given me an interesting scenario to think about. It's an unusual case but valid.Kelly S. French– Kelly S. French2019年05月08日 18:47:43 +00:00Commented May 8, 2019 at 18:47
-
1Here is another example: Java allows you to pre-allocate a block of memory (starting heap size?) at load-time for exactly the same reason, to cut down on memory allocation during execution. This would actually make for a good general StackOverflow question for the general case and I'd encourage you to post about it.Kelly S. French– Kelly S. French2019年05月09日 15:05:48 +00:00Commented May 9, 2019 at 15:05
2 Answers 2
Sure. Using new
or malloc
to set things up initially is really no different to static allocations. There is nothing inherently bad with using malloc
or new
, only in using it repeatedly with free
or delete
.
Allocating a block of heap memory and keeping it allocated for the rest of eternity does not cause heap fragmentation. It's only repeated allocation and deallocation of variable sizes (such as String
causes) that start to create problems. Allocating at the start of your program and leaving it allocated, as long as you don't allocate too much, of course, is not a problem.
-
Thanks for the confirmation. The only disadvantage I can see is that the used memory is not calculated to the global variables in the Arduino IDE.Michel Keijzers– Michel Keijzers2019年05月08日 09:23:43 +00:00Commented May 8, 2019 at 9:23
-
1Yup. Hence the nudge to watch out that you don't allocate too much :)Majenko– Majenko2019年05月08日 09:30:47 +00:00Commented May 8, 2019 at 9:30
-
Maybe I will for debugging purposes show the amount of reserved memory after the new statements.Michel Keijzers– Michel Keijzers2019年05月08日 09:35:43 +00:00Commented May 8, 2019 at 9:35
In the destructor you would need
LightSetup::~LightSetup()
{
for (int n = 0; n < NR_OF_PARS; n++)
{
delete _pars[n];
}
}
and every time you replace a _pars you need to delete the old one. This can be done automatically by using unique_ptr.
However for your purpose it sounds like a #if
is better suited to differentiate at compile time which type you actually store inside that array.
#if ON_ARDUINO
Par _pars[NR_OF_PARS];
#else
TestPar _pars[NR_OF_PARS];
#endif
-
Well, the destructor will never be called, since in my Arduino application the loop will run forever, so it's not an issue. But I upvoted it for the last part, maybe that's even better than the new command (although it clutters up code a bit). However, if this is the only change, it is easier. ThanksMichel Keijzers– Michel Keijzers2019年05月08日 09:49:44 +00:00Commented May 8, 2019 at 9:49
-
1I agree with @ratchet on the #if as the case you describe isn't about dynamic typing but rather a difference in platforms. Unless you need the same binary to run on both platforms (if even possible) you would be better served by conditional compilation. This also removes the need to bother with a destructor.Kelly S. French– Kelly S. French2019年05月08日 15:32:05 +00:00Commented May 8, 2019 at 15:32