virtuelle Fkt.: Funktionalität in Basisklasse mit Fallunterscheidung



  • Hallo,

    ich hatte gedacht, dass ich das Konzept der Polymorphie & virtuellen Methoden soweit ganz gut verstanden hätte, stehe aber gerade vor einem Problem, auf das mir so recht keine Lösung einfällt außer Code zu duplizieren.

    Also, Basisklasse hat einen Event-Handler f. Maus-Klicks (ist ein Qt-Projekt), der in abgeleiteter Klasse überschrieben werden bzw. erweitert werden soll:

    class NoisyButton : public QPushButton
    {
        Q_OBJECT
    public:
        NoisyButton(QWidget* parent = 0);
    
        virtual ~NoisyButton();
    
        bool isFunctional() const {
            return mFunctional;
        }
    
    [...]
    protected slots:
        virtual void mousePressEvent(QMouseEvent*);
    
    [...]
    private:
        Sound* mSound;
        bool mFunctional;
    };
    
    NoisyButton::mousePressEvent(QMouseEvent* e)
    {
        if (!isFunctional())
        {
            mSound->playback("disabled.wav");
            e->accept();
        }
        else
        {
            e->ignore();
            QPushButton::mousePressEvent(e);
        }
    }
    

    Jetzt habe ich abgeleitete Klassen SimpleButton, MultiButton, die von NoisyButton erben. Während SimpleButton nichts tut (ist im wesentlichen eine leere Klasse), soll MultiButton eine Mehrfachbelegung des Buttons simulieren (durch mehrfache Klicks wie beim Handy-SMS). Später sollen die Buttons als NoisyButton-Zeiger in einen QVector abgespeichert werden:

    QVector<NoisyButton*> mButtons;
    
    class SimpleButton : public NoisyButton
    {
        Q_OBJECT
    public:
        SimpleButton(QWidget* parent = 0);
    
        virtual ~SimpleButton();
    };
    
    class MultiButton : public NoisyButton
    {
        Q_OBJECT
    public:
        MultiButton(QWidget* parent = 0);
    
        void setBtnText(const QStringList& t);
    
        int interval() const {
            return mClickInterval;
        }
    
        int acceptInterval() const {
            return mAcceptTimer.interval();
        }
    
    signals:
        void clicked(unsigned count);
    
    public slots:
        void setInterval(int i = 1000);
    
        void setAcceptInterval(int i = 2000);
    
    protected slots:
        virtual void mousePressEvent(QMouseEvent*);
    
        void emitAccept();
    
    private:
        unsigned mClicks;
        QStringList mBtnText;
        int mClickInterval;
        QTime mElapsedTime;
        QTime mPressTime;
        QTimer mAcceptInterval;
        int mLastButtonState;
    };
    
    MultiButton::MultiButton(QWidget* parent)
        : NoisyButton(parent)
    {
        setAcceptInterval();
        setInterval();
        mAcceptTimer.setSingleShot(true);
        connect(&mAcceptTimer, SIGNAL(timeout()), this, SLOT(emitAccept()));
    }
    void MultiButton::mousePressEvent(QMouseEvent* e)
    {
        mLastButtonState = e->button();
    
        if (e->button() & Qt::LeftButton)
        {
            if (mPressTime.elapsed() < mClickInterval)
            {
                mClicks++;
                mClicks = mClicks % mBtnText.size();
            }
            mAcceptTimer.start();
            mPressTime.restart();
        }
    }
    
    void MultiButton::emitAccept()
    {
        if (mClicks % mBtnText.size() == 0)
            mClicks = mBtnText.size();
        emit clicked(mClicks);
        mAcceptTimer.stop();
        mClicks = 0;
        setText(mBtnText[mClicks % mBtnText.size()]);
    }
    

    Das Problem ist nun, dass ich den MouseHandler-Slot der Basisklasse in der abgeleiteten Klasse ebenfalls ausführen muss, die Funktionalität der virtuellen Funktion aber nur im if-Zweig stattfinden darf.
    Wie schaffe ich es, die Basisklassen-Funktionalität in der abgeleiteten Klasse ebenfalls auszuführen, aber eben abhängig vom Zustand von mFunctional? Oder geht das nur mittels Code-Duplizierung, was ja dem Vererbungskonzept widersprechen würde?

    Danke für alle Hinweise,
    AlGaN



  • Hallo,

    ohne jetzt auf deinen Code einzugehen:

    Wie schaffe ich es, die Basisklassen-Funktionalität in der abgeleiteten Klasse ebenfalls auszuführen,

    Indem du die Basisklasse direkt ansprichst

    void Derived:fkt()
    {
      Base::fkt();
    }
    

    Oder du nutzt das NVI-Idiom und packst den 'immer ausführen' Teil,
    in die nicht-virtuelle Funktion.



  • Ich bin mir nicht ganz sicher, ob ich alles richtig verstanden habe, aber ich versuch es mal:
    Wenn du von deinem Vater schwarze Haare erbst und färbst sie blond, ist das schwarz weg. So ist das hier auch: überschreibst du die Methode in der Tochter, wird auch nur die Tochter ausgeführt. Die Mami-Methode ist "weg". Willst du an die Mami ran, könnte das glaub ich die Tochter in Ihrer Methode über sowas wie parent->Methode() oder so.
    Hab schon länger nix mehr geerbt, müsst ich jetzt nachgucken...



  • Und als Ergänzung: mousePressEvent ist kein SLOT!



  • Hallo,

    @Jockelx: Okay, mein Code ist nicht der beste, vll. hab ich auch einen Design-Fehler drin, mir ist bis jetzt nur keine andere Lösung eingefallen, SimpleButtons und MultiButtons über ein gemeinsames Interface anzusprechen.

    Das NVI-Idiom hab ich mir angeschaut, weiß aber nicht, ob es den Code-Bloat rechtfertigt für meinen refactored-Teil der Basisklasse, der ja im wesentlichen nur aus der Fallunterscheidung besteht...

    @l'abra d'or: Okay, war ein Schreibfehler, im originalen Code hab ich den Handler im normalen protected-Abschnitt drin...



  • Verwende doch einfach eine neue, eigene virtuelle Funktion:

    class NoisyButton : public QPushButton
    {
        Q_OBJECT
    public:
        NoisyButton(QWidget* parent = 0);
    
        virtual ~NoisyButton();
    
        bool isFunctional() const {
            return mFunctional;
        }
    
    [...]
    protected:
        virtual void mousePressEvent(QMouseEvent*);
        virtual void noisyButtonMousePressEvent(QMouseEvent*);
    
    [...]
    private:
        Sound* mSound;
        bool mFunctional;
    };
    
    void NoisyButton::mousePressEvent(QMouseEvent* e)
    {
        if (!isFunctional())
        {
            mSound->playback("disabled.wav");
            e->accept();
        }
        else
        {
            noisyButtonMousePressEvent(e);
            QPushButton::mousePressEvent(e);
        }
    }
    
    // Default-Implementierung, kann von abgeleiteten Klassen überschrieben werden
    void NoisyButton::noisyButtonMousePressEvent(QMouseEvent* e)
    {
        e->ignore();
    }
    

    Ob der Aufruf von QPushButton::mousePressEvent(e) und/oder e->ignore() immer, also für alle von NoisyButton abgeleiteten Controls erfolgen soll, kann ich schwer beurteilen, da ich Qt nicht kenne, und auch nicht genau weiss was du vor hast.
    (Alles was immer erfolgen soll/muss sollte in NoisyButton::mousePressEvent passieren, alles was nicht immer erfolgen soll/muss in NoisyButton::noisyButtonMousePressEvent - meine Wahl war mehr oder weniger beliebig)


Anmelden zum Antworten