====== Avoid new/delete, prefer references to pointers, use member variables for sub-Components ====== The JUCE library includes a nice collection of //managed pointer// template classes, such as //juce::ScopedPointer//, which can and should be used to avoid most uses of the ''delete'' keyword. Note that managed pointers are still assigned using ''new''. I checked my code carefully, and was unable to find any uses of ''delete'', and found none. I did find a number of uses of ''new'', however, which fell into two categories: - In my //VanillaJuceAudioProcessor// class, I used ''new'' to create object-instances where there was effectively no choice; the JUCE library itself required it. I also used ''new'' when when creating //juce::XmlElement// objects, following the example of the JUCE static function //juce::AudioProcessor::getXmlFromBinary()//, which returns a non-managed pointer. - In all of my GUI-related classes, I used ''new'' to create various //juce::Component// objects, assigning the result to //juce::ScopedPointer// member variables of the //Gui...// class. I used the Projucer to generate this code, and that's what it produced, so I assumed this was the recommended approach. I have since learned, through discussion on the JUCE Forum, that this is definitely not the case. Regarding the first category, I can only assume that Jules's admonition to "ditch all your new/delete..." was something of a generic complaint. Regarding the GUI-related classes, I now understand that the Projucer's lavish use of //ScopedPointer// is not at all recommended, and that child //Component// objects should ideally be declared as member variables. ===== Cleaning up the GUI classes ===== JUCE Forum user //Holy_City// provided the following code-example showing a variety of tricks for reducing the size of constructor functions for //juce::Component//-derived classes: lass EnvelopeComponent : public Component { public: EnvelopeComponent() /* user the initializer list */ : attack (Slider::Rotary, Slider::noTextBox), //very helpful constructor for Sliders decay (Slider::Rotary, Slider::noTextBox), sustain (Slider::Rotary, Slider::noTextBox), release (Slider::Rotary, Slider::noTextBox) { auto initSlider = [this] (Slider& s) // lambda expression declaration { addAndMakeVisible (s): s.setRange (1.0, 500.0); s.setValue (25.0); }; initSlider (attack); initSlider (decay); initSlider (release); addAndMakeVisible (sustain); sustain.setRange (0.0, 1.0); sustain.setValue (0.707); } //.... yada yada yada private: Slider attack, sustain, decay, release; /* don't need to put them on different lines */ }; Following //Holy_City//'s example I was able to reduce the //GuiEgTab// class from this: class GuiEgTab : public Component, public SliderListener { public: GuiEgTab (SynthSound* pSynthSound); ~GuiEgTab(); void paint (Graphics& g) override; void resized() override; void sliderValueChanged (Slider* sliderThatWasMoved) override; void notify(); private: SynthSound* pSound; ScopedPointer to this: class GuiEgTab : public Component, public SliderListener { public: GuiEgTab (SynthSound* pSynthSound); void paint (Graphics& g) override; void resized() override; void sliderValueChanged (Slider* sliderThatWasMoved) override; void notify(); private: SynthSound* pSound; Label attackLabel, decayLabel, sustainLabel, releaseLabel; Slider attackSlider, decaySlider, sustainSlider, releaseSlider; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GuiEgTab) }; GuiEgTab::GuiEgTab (SynthSound* pSynthSound) : pSound(pSynthSound) , attackLabel("attack", TRANS("Attack Time (sec)")) , decayLabel("decay", TRANS("Decay Time (sec)")) , sustainLabel("sustain", TRANS("Sustain Level (%)")) , releaseLabel("release", TRANS("Release Time (sec)")) { auto initLabel = [this](Label& label) { addAndMakeVisible(label); label.setFont (Font (15.00f, Font::plain).withTypefaceStyle ("Regular")); label.setJustificationType (Justification::centredRight); label.setEditable (false, false, false); label.setColour (TextEditor::textColourId, Colours::black); label.setColour (TextEditor::backgroundColourId, Colour (0x00000000)); }; initLabel(attackLabel); initLabel(decayLabel); initLabel(sustainLabel); initLabel(releaseLabel); auto initSlider = [this](Slider& slider) { addAndMakeVisible(slider); slider.setSliderStyle (Slider::LinearHorizontal); slider.setTextBoxStyle (Slider::TextBoxRight, false, 80, 20); slider.addListener (this); }; initSlider(attackSlider); attackSlider.setRange (0, 10, 0); initSlider(decaySlider); decaySlider.setRange (0, 10, 0); initSlider(sustainSlider); sustainSlider.setRange (0, 100, 1); initSlider(releaseSlider); releaseSlider.setRange (0, 10, 0); notify(); } void GuiEgTab::paint (Graphics& g) { g.fillAll (Colour (0xff323e44)); } void GuiEgTab::resized() { const int labelLeft = 16; const int controlLeft = 144; const int labelWidth = 120; const int sliderWidth = 420; const int controlHeight = 24; const int gapHeight = 8; int top = 20; attackLabel.setBounds (labelLeft, top, labelWidth, controlHeight); attackSlider.setBounds (controlLeft, top, sliderWidth, controlHeight); top += controlHeight + gapHeight; decayLabel.setBounds (labelLeft, top, labelWidth, controlHeight); decaySlider.setBounds (controlLeft, top, sliderWidth, controlHeight); top += controlHeight + gapHeight; sustainLabel.setBounds (labelLeft, top, labelWidth, controlHeight); sustainSlider.setBounds (controlLeft, top, sliderWidth, controlHeight); top += controlHeight + gapHeight; releaseLabel.setBounds (labelLeft, top, labelWidth, controlHeight); releaseSlider.setBounds (controlLeft, top, sliderWidth, controlHeight); } void GuiEgTab::sliderValueChanged (Slider* sliderThatWasMoved) { double value = sliderThatWasMoved->getValue(); SynthParameters* pParams = pSound->pParams; if (sliderThatWasMoved == &attackSlider) pParams->ampEgAttackTimeSeconds = value; else if (sliderThatWasMoved == &decaySlider) pParams->ampEgDecayTimeSeconds = value; else if (sliderThatWasMoved == &sustainSlider) pParams->ampEgSustainLevel = 0.01 * value; else if (sliderThatWasMoved == &releaseSlider) pParams->ampEgReleaseTimeSeconds = value; pSound->parameterChanged(); } void GuiEgTab::notify() { SynthParameters* pParams = pSound->pParams; attackSlider.setValue(pParams->ampEgAttackTimeSeconds); decaySlider.setValue(pParams->ampEgDecayTimeSeconds); sustainSlider.setValue(100.0 * pParams->ampEgSustainLevel); releaseSlider.setValue(pParams->ampEgReleaseTimeSeconds); }