Derived Component object and ScopedPointer


#1

Hi all,

Can I ‘ScopedPointer’ a derived Component object? I ask this because I have tried so and it did not work to me. So, I do not if I have coded something wrong in my source or it is not feasible by design.

Thank you as usual,

Gabriel


#2

You can ScopedPointer pretty much any object you desire… including objects whose base-class is juce::Component.

Are you getting compiler errors, or run-time errors?


#3

Thank you, jrlangois,

I don’t get compile errors. It is just only that when I encapsule the component, it doesn’t visualize in their window container. But when I instantiate without ScopedPointer everything works fine.

I’ll investigate, for sure, I have a subtle error somewhere. Thank you!

Gabriel


#4

Do you have some simple example code which doesn’t work? I presume you are using this as a class member. If you have code which works in the following way:

[code]//Class Declaration:
private:
MyComponent* myComponent;

// Class Implementation:
MyClass::MyClass()
{
myComponent = new MyComponent();
}

MyClass::~MyClass()
{
delete myComponent;
}[/code]

You can just replace the declaration to:

private: ScopedPointer<MyComponent> myComponent;
And obviously remove the delete statement from the destructor.

A couple of tips however:

  • If you are using the object as a class member and it has a relatively straightforward constructor, use a stack object rather than a dynamically allocated one i.e. no pointer involved. I know this isn’t always possible (e.g. if you are relinquishing ownership at some point) but is preferable in most cases.
  • Always use some form of the JUCE_LEAK_DETECTOR macros in your classes. These can quickly identify common lifetime issues.
  • Read the comment above the ScopedPointer (const ScopedPointer&) copy constructor in the class header file (near the bottom). If you are creating and assigning the ScopedPointer in a method you will need to explicitly invoke the copy constructor rather than using the assignment operator.
  • Remember that ScopedPointer calls delete, not delete[] on the object so can not be used for storing arrays.

I know some of these points don’t sound like your problem but are useful things to remember and can lead to quicker debugging.

Re-reading your post it sounds like you may have been using a stack object which was working. This suggests you just aren’t instantiating the object and assigning it to the pointer. Do you have this line somewhere ‘myComponent = new MyComponent();’? If not I am surprised that your program isn’t crashing when you de-reference the pointer. Are you definitely setting its bounds in resized()?


#5

Dear David,

I think I have done everything exactly you have related. Here, it is some source fragments after the modifications you have suggested to me:


class OnTimeInstaller  : public Component,
public ChangeListener,
public ExitMessage
{
public:
    //==============================================================================
    OnTimeInstaller ();
    ~OnTimeInstaller();
    
    //==============================================================================
    //[UserMethods]     -- You can add your own custom methods in this section.
    //[/UserMethods]
    
    void paint (Graphics& g);
    void resized();
        
    //==============================================================================
    juce_UseDebuggingNewOperator
    
private:
    //[UserVariables]   -- You can add your own custom variables in this section.

    ...

       ScopedPointer<OTIWelcomeFirst> WelcomeFirst;

    ...

    //[/UserVariables]
    
}    

OnTimeInstaller::OnTimeInstaller ()
    : labelInfo (0),
      cachedImage_ontime401_png (0)
{

    ...

    //[UserPreSize]
    //ScopedPointer<OTIWelcomeFirst> WelcomeFirst;
    WelcomeFirst = new OTIWelcomeFirst( this );
    addAndMakeVisible ( WelcomeFirst );
    WelcomeFirst->setBounds (0, 112, 600, 188); 
 
   ...

This way, everything works flawlessly!

Finally, thank you for the list of tips you wrote. Do you mind to enhance your explanation of why is better an stack object than a dynamic one. This is an issue that rounds in my head but I do not have criteria to choose to the best option.

Gabriel


#6

There are quite a large number of reasons and I’m sure others will have their own but here are the ones I can think of off the top of my head:

  1. Jules says so. Take a look at the JUCE style guide, it has some great information in it and is always nice to have a style guide so we don’t have to think about these things, it leaves more time for coding. Here is the relating rule: “Do not use ‘new’ unless there’s no alternative. Whenever you type ‘new’, always treat it as a failure to find a better solution. If a local variable can be allocated on the stack rather than the heap, then always do so.

  2. For comparison I always think of primitive types. You wouldn’t create a pointer to an int as a member and then allocate a single int on the heap and assign it to that pointer. Why do this for other types?

  3. It saves lines of code and therefore potential errors. When using a stack member you don’t have to remember to instantiate it or delete it. It is essentially a ScopedPointer with automatic instantiation.

  4. You don’t have to worry about the pointer being null and causing potential crashes. Having a stack object (or a reference which is also preferential) means you can assume it is a valid object for the lifetime of that function or class. This means not doing lots of if (mypointer != nullptr) all over the place.

  5. I much prefer the dot (.) operator over the indirect (->) operator. It just looks and reads nicer.

  6. It may in theory be slightly faster to access the member as there is no pointer de-reference. In reality this isn’t really a good reason and the performance gain is negligible over the improved readability and coding style. Internal compiler optimsation is a dark art and often compensates for these sort of things so its best to not try and second guess them. Always follow the rules of optimisation.

Going back to your example I presume the commented out lines are the ones which didn’t work. In this instance the scope of the ScopedPointer is only to the constructor. Although you successfully allocate, add the component and set its bounds, the component will be deleted as soon as the ScopedPointer goes out of scope, at the end of the constructor. If you used a normal pointer here you would be leaking the object as you no longer have any reference to it which you can delete.

To avoid problems like this, or at least make them easier to debug I always set my components bounds in the resized() method. Firstly, here you know the parent’s width and height so you can better position your components and because that would have caused your first example to crash as the pointer is no longer valid. A crash is the best form of bug because you have a head start in finding out what is causing it.

I hope all this makes sense and is useful. JUCE is a really good teaching tool for all these good practices. As a general rule I always look at the JUCE codebase to see how Jules has done something before attempting something similar myself.


#7

David, you have mastered and commanded this issue.

I have taken good note of your recommendations.

Gabriel


#8

The reason I would create graphical objects on the heap is because they tend to stick around for a while - as in, have a long lifetime.