Best practices for inheriting from JUCE classes?

Hi all, JUCE and C++ noob here.

I am trying to create a custom slider that initializes with setter methods according to arguments passed in to the constructor. Like this:

// declaration
class Knob : public juce::Slider { public: Knob (string suffix); }
// definition
Knob::Knob (string suffix) { this->setTextValueSuffix(suffix); }

The problem comes when I declare an instance in the PluginEditor header file.

Knob knob; // expects default constructor w/out arguments

After a bit of searching it looks like I have a couple options. 1) Declare with default constructor and then call an init function in the editor constructor. 2) Declare with a pointer and then call new.

Option 1 is often recommended online but seems awfully clunky and kind of defeats the purpose of a constructor. But Option 2 allocates on the heap and requires manual cleanup.

I’m coming from Python where I never have to deal with this stuff. Am I overthinking it? Does it matter? In general if I want to modularize GUI components, say for a multiband EQ where each band has its own gain, Q, frequency, shape settings, what is the cleanest way forward if I want to parameterize objects instead of copy-pasting code in the editor file?

TYIA for any words of wisdom.

If you know the arguments you want to pass to the Knob constructor at the time the editor is constructed, then there’s no reason to delay construction of the Knob object or use 2-stage initialization, just pass those parameters directly to the Knob constructor:

// editor.h:
struct Editor : public juce::AudioProcessorEditor
{
    Knob knob;
};

// editor.cpp:
Editor::Editor
: knob("This knob's suffix") // this argument gets passed to the Knob constructor
{
}
1 Like

if you have a reason to do defer like this you should instead declare as std::unique_ptr<Class> and then use std::make_unique<Class>(...args...) to assign to it.

It’s very rare that you should ever need to call new in modern C++.

1 Like

Thanks. I thought I had to do everything within the curly brackets. I don’t fully understand what is happening, syntactically, in the second line of editor.cpp where knob is constructed. In the header declaration the colon denotes inheritance, but in the constructor in editor.cpp it’s used to initialize members?

Yes, that’s called an initializer list.

1 Like

Just being pedantic but

Foo::Foo()
    : a{ 1 }
    , b{ 2 }
    , c{ 3 }
{
}

Is a member initializer list.

Whereas

std::vector<int> v = { 1, 2, 3 };

Is an initializer list.

1 Like