Couple of beginner questions about best pointer practices


#1

Firstly, I am getting confused about the best way to handle storing component objects:

[list]
[]The audio plugin demo uses normal “pass-by-value” for components (this seems more natural to me). There is nothing to worry about in the destructor.[/]
[]The Juce Demo uses pointers to store components, and uses deleteAndZero in the destructor (which the wiki strongly discourages)[/][/list]
Which (if either) of these methods is preferable, are there certain advantages / disadvatages to each? For example I sometimes find myself storing sets of repetitive components in arrays. Does either method lean itself to this more?

Secondly, I am in the situation where I have a number of large Sample objects (my own class which contains, amongst other things, an AudioSampleBuffer of data) which I want to store as a pool using an Array object. Currently, as I don’t want to be making copies of the data, I’m doing:

File testFile = File("C:\\Users\\Hemmer\\Desktop\\funky.wav"); Array<AudioSample*> samplePool = Array<AudioSample*>(); samplePool.add(new AudioSample(testFile));

I’m sure it was drilled into me that when you use “new” there should always be a corresponding “delete”. Should I be worried about this and if so, how do I get around this? Also if I want to go about clearing the both array and the data / memory in it, which is the best way to go about this?

Any advice/input greatly appreciated, Hemmer


#2

I think the best method is to use ScopedPointers as class members.
instead of :

declarations (.h)

class MainView  : public Component,
                  public ApplicationCommandTarget
{
public:
    //==============================================================================
    MainView ();
    ~MainView();

    //==============================================================================
Button* myButton;

// blablabla 

definitions (.cpp)

MainView::MainView ()
    : Component (T("Main  View")),
      myButton(0)
{
    addAndMakeVisible (myButton = new Button ());
}

MainView::~MainView()
{
   deleteAndZero (myButton);
}

…you should use :
declarations (.h)

class MainView  : public Component,
                  public ApplicationCommandTarget
{
public:
    //==============================================================================
    MainView ();
    ~MainView();

    //==============================================================================
ScoppedPointer<Button> myButton;

// blablabla 

definitions (.cpp)

MainView::MainView ()
    : Component (T("Main  View")),
      myButton(0)
{
    addAndMakeVisible (myButton = new Button ());
}

MainView::~MainView()
{
}

See how the delete disapeared ? That’s what Jules means in his code comments when he says "always use RAII"
This is a better approach because

  • you obviously don’t have to delete manually
  • it’s working even when the code follows a non-trivial path (think exceptions for example)

It’s also a good practice to use “JUCE_LEAK_DETECTOR()” on all your classes, so you know at shutdown, if you leaked anything.

[quote]I’m sure it was drilled into me that when you use “new” there should always be a corresponding “delete”. Should I be worried about this and if so, how do I get around this? Also if I want to go about clearing the both array and the data / memory in it, which is the best way to go about this?
[/quote]

This is a matter of pointer ownership (look it up, you’ll find plenty of usefull information)
The one who deletes is the one who owns.

Array doesn’t owns the objects, so if you “new” something into an array, you will have to delete it.
If you use OwnedArray for example, your array will own the objects, so you don’t need a delete matching your new. If you put an object into an OwnedArray, it will be deleted when the array is destroyed

Good luck with that !
HTH


#3

Thanks, lots of solid advice there. Interestingly, Visual Studio 2010 was marking the code below with the errors (despite compiling fine):

Error: a value of type “juce::Slider *” cannot be assigned to an entity of type “juce::ScopedPointerjuce::Slider *”.
Error: argument of type “juce::ScopedPointerjuce::Slider *” is incompatible with parameter of type “juce::Component *”.

// gainSlider is type ScopedPointer<Slider> gainSlider = new Slider("gain"); addAndMakeVisible(gainSlider = new Slider("gain"));

The solution (should others come across this) is just to rescan the solution, I think it just rebuilds the Intellisense database.


#4

There are a few places where the demo uses jucer-generated components, and the jucer does generate deleteByZero calls. That’s something that’ll change in the future!

BTW, your code:

File testFile = File("C:\\Users\\Hemmer\\Desktop\\funky.wav"); Array<AudioSample*> samplePool = Array<AudioSample*>(); samplePool.add(new AudioSample(testFile));

…is pretty nasty. Looks like you need to brush up on your C++ object initialisation syntax. Try:

File testFile ("C:\\Users\\Hemmer\\Desktop\\funky.wav"); OwnedArray<AudioSample> samplePool; samplePool.add (new AudioSample(testFile));

…and although dinaiz’s example is ok, it could be done more clearly and efficiently like this:

[code]class MainView : public Component,
public ApplicationCommandTarget
{
public:
MainView ()
{
addAndMakeVisible (&button);
}

private:
Button myButton;
};
[/code]

(Obviously Button is an abstract class so this code won’t compile, but I used the same names for comparison with the code above)


#5

So would you basically say that ScopedPointers would be overkill for normal components (i.e. easier to just have normal objects which are automatically handled when the UI class is destroyed)?

The OwnedArray looks like exactly what I need to use in quite a few places actually, thanks for the tip.

BTW that code segment was just my poor attempt to illustrate the array type, in my code the array should be getting initialised properly in the constructor, but thanks for the solid example anyway!


#6

If you can use a stack-based or member object, then you should do so.
If you can’t, then use a ScopedPointer or some other automatic container.

There are really very very few times you’d ever need to use delete directly. In the juce codebase I think there’s maybe 100 deletes, and most of those are inside classes like ScopedPointer itself!


#7

Oh yeah, actually my code wasn’t meant to be used, but instead to illustrate the ScopedPointer concept (Cargo cult programming should also be avoided in most cases :slight_smile: