Array/HeapBlock usage questions


#1

Hi,

I’ve recently started to try to use Juce’s Array and HeapBlock to avoid using new and delete, which was my MO until I read some disparaging comments by Jules. This is the first time I’ve made a lot of use of smart pointers and the like in C++, so I have some questions about using them properly…

  1. When using an Array as a class member to hold instances of another class, the only way I’ve gotten it to work was to make an Array<TheClassName*> classArray. Then, when populating the array, do classArray.add( new TheClassName() );. Is this the best way to do it? Will the Array delete those news appropriately when the class for which it’s a member is destructed? To give context, one place I’m doing this is in an AudioProcessor where the same effect is being applied to all channels. So, I’ve created a class (Processor) which does the processing, and the AudioProcessor has a class member (processors) which is an Array of these processor classes. The first time prepareToPlay is called, I do
for ( int n = 0; n < getNumInputChannels(); n++ )
{
    processors.add( new Processor() );
}

Is this smart or woefully misinformed?

  1. I used to use a lot of raw pointer arrays in my processing… so the Processor above would have, say, a class member float* someBuffer, and in the Processor constructor, there would be someBuffer = new float[numSamples]; and in the destructor there would be a delete someBuffer. So, in the spirit of getting rid of new/delete, I first tried using an Array instead. But, I found that there huge increase in CPU (maybe fivefold) due to all of the calls to Array’s [] operator, which checks the index based on the array bounds in addition to just being more expensive than a raw array access (among other things). So, I tried buffer.getRawDataPointer()[index], which also had a lot of overhead, and then I tried switching to a HeapBlock, which still had (less) overhead… finally I have arrived at using HeapBlocks, but having a class member both for the HeapBlock AND the HeapBlock’s raw data pointer. So I have something like
HeapBlock<float> window;
float* windowRaw;

and then in the constructor

window.allocate( numSamples, false );
windowRaw = window.getData();

and then when I want to access a member of window I do window[n]. This has removed virtually all of the overhead but it looks pretty ugly and I can’t imagine it’s the best practice…

  1. When I’m using a HeapBlock as I was just describing, will it take care of freeing itself? Eg, if I allocate in the class constructor do I need to free in the destructor, just like new/delete or is it smart enough to free itself? Same question for Arrays, after .add()ing a bunch of stuff.

Sorry for the long-winded post, it’s been a fun experience learning this stuff and I think I’m getting closer and closer to writing some nice code. Thanks in advance for any thoughts.


#2
  1. if you want the object deleted at array destruction you need to look at OwnedArray

#3

Glad you’re discovering RAII! If only everyone would do that…

  1. Why on earth are you storing a separate pointer to the heapblock!? A heapblock is already just a pointer, and calling getData() or casting it to retrieve the pointer will have zero overhead!

  2. Heapblock is just for blocks of raw memory - it uses malloc and free, so you can’t use it for arrays of objects that have constructors or destructors.


#4

Thanks to both of you for your replies.

OK - great. Would you say that the technique I’m using (an Array or OwnedArray of pointers to a class, populated with new’s) is sane?

Well, I was finding that using the [] operator on the HeapBlock actually did have some overhead… eg

int nSamples = 1024;
HeapBlock<float> samples;
samples.allocate( nSamples, false );
float* samplesRaw = samples.getData();
for (int n = 0; n < nSamples; n++)
{
  // This calls the HeapBlock [] operator, which then retrieves the float in memory and returns it
  samples[n] = 1.0;
  // This just retrieves the float in memory - no call to another function.
  samplesRaw[n] = 1.0;
}

The additional per-sample call to the [] operator function was doubling CPU usage in my case… I’m fairly positive. Something else could be going on - but switching from HeapBlock [] operator dropped CPU significantly. Is this reasonable at all?

[quote=“jules”]
3) Heapblock is just for blocks of raw memory - it uses malloc and free, so you can’t use it for arrays of objects that have constructors or destructors.[/quote]

Sorry, I don’t think I was clear, here’s what I mean:

class SomeClass
{
public: 
  HeapBlock<float> samples;
  Array<float> feet;
  SomeClass()
  {
    samples.allocate( 1000, true );
    feet.add( 1.0 )
  }
  ~SomeClass()
  {
    // Do I need to call free here?
    samples.free()
    // Do I need to call clear here?
    feet.clear()
  }
}

Thanks again for any tips.


#5

…in a debug build, that’s reasonable, but I’d be astounded if any modern C++ compiler would fail to inline such a simple method in a release build!

Re: calling free() - no, there’s no need: the HeapBlock’s destructor will free the memory automatically. That’s the whole point, really… if it didn’t do that, there’s be no reason for the class to exist!. As long as you delete the HeapBlock, it’s impossible for it to leak memory.


#6

[quote=“jules”]
…in a debug build, that’s reasonable, but I’d be astounded if any modern C++ compiler would fail to inline such a simple method in a release build![/quote]

Ah, that makes sense, that must have been it.

[quote=“jules”]
Re: calling free() - no, there’s no need: the HeapBlock’s destructor will free the memory automatically. That’s the whole point, really… if it didn’t do that, there’s be no reason for the class to exist!. As long as you delete the HeapBlock, it’s impossible for it to leak memory.[/quote]

OK, that’s what I figured, and that definitely makes sense - just wanted to make sure I wasn’t misunderstanding the usage and leaking memory inadvertently…

Thanks a billion for the help Jules.


#7

i think that it’s what those classes are for, that’s what i use them for anyway.


#8

[quote=“atom”]
i think that it’s what those classes are for, that’s what i use them for anyway.[/quote]

Good! Thanks for the reassurance.


#9

Take a look at ReferenceCountedObjectPointers and ReferenceCountedObjectArrays. These classes will help with instance lifetimes, specifically (when used properly):

  • if something goes wrong (exception, dropped pointer) they will clean up properly,

  • you can pass pointers to your object around without worrying about lifetimes so much, e.g. will I accidentally delete it from the OwnedArray when someone else is using it.

  • You can delete some elements from your array without worrying about who else knows about it

  • you can’t accidentally delete or lose the instances.

It’s equivalent to HeapBlock for class instances, in many ways.

Bruce


#10

Thanks for the tip Bruce, I will look into both.

Just to confirm, using HeapBlock’s [] operator in a Release build does not have a substantial CPU overhead. Nice!