Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Frage zum Thema private implementation



  • Hallo zusammen,
    um in das Thema QtQuick / QML zu kommen, hatte ich mir ein Buch aus dem packt Verlag besorgt. In diesem verwendet der Autor die private implementation Technik mit folgendem Beispiel:

    private:
        class Implementation;
        QScopedPointer<Implementation> implementation;
    

    in der Klassendefinition.

    Jetzt habe ich heut noch gelesen, dass es noch eine weitere Methode gibt:

    class GeoControllerPrivate;
        class LIBGEO_EXPORT GeoController : public QObject
        {
            Q_OBJECT
        Q_DECLARE_PRIVATE(GeoController);
        QScopedPointer<GeoControllerPrivate> const d_ptr;
    ...
    

    Gibt es denn eine Aussage, welche die richtige Methode ist? Gibt es im Netz eine Doku zu diesem Thema? Warum mache ich denn generell eine private Implementierung?

    Danke für Eure Hilfe.
    gruss
    martin



  • Den Qt-Weg erklärt ein Artikel in der Wiki.

    @msauer751

    Gibt es im Netz eine Doku zu diesem Thema? Warum mache ich denn generell eine private Implementierung?

    Du findest sehr viel zu diesem Thema, wenn Du im Netz nach "d-zeiger idiom" oder "pimpl idiom" suchst.


  • Lifetime Qt Champion

    Hi, @msauer751

    Q_DECLARE_PRIVATE ist wie Q_OBJECT ein Makro. Such mal nach der Deklaraton und versuche sie zu verstehen.

    Warum treibt man den Aufwand: Binärkompatibilität.

    Wenn du einer Klasse Membervariablen hinzufügst, enfernst oder änderst müssen alle Nutzer neu compiliert werden. Mit PIMPL (pointer to private implementation) kann das vermieden werden.

    Grüsse



  • Hi,

    danke Euch schonmal für die Infos.

    D.h. der Weg über class Implementation ist der allgemeine Weg und bei Verwendung von Qt kann alles einfacher über die Macros gemacht werden?

    gruss
    martin


  • Lifetime Qt Champion

    @msauer751 Ja, das kann man so sagen.

    Grüsse



  • So,
    habe jetzt mal meine Klasse auf Q_DECLARE_PRIVATE / Q_DECLARE_PUBLIC umgestellt. In dem privaten Teil würde ich gern ein Signal mit einem Slot verbinden. In der Public Funktion habe ich dazu das connect aufgerufen:

        connect(d->qGeoCodeReply, SIGNAL(finished()), this, SLOT(searchDone()));
    

    Allerdings bekomme ich immer die Meldung,

    Object::connect: No such slot geo::GeoController::searchDone() in ../../lib-geo/source/controller/geo-controller.cpp:108.

    Hier mal mein Code:

    include.h:

    #ifndef GEO_H
    #define GEO_H
    
    #include <QObject>
    #include <QGeoServiceProvider>
    #include <QGeoAddress>
    #include <QGeoCodeReply>
    #include <QGeoLocation>
    #include <QGeoCodingManager>
    #include <QString>
    #include <QDebug>
    #include <QList>
    
    #include "lib-geo_global.h"
    #include "data/geo-point.h"
    
    namespace geo {
    
    class GeoControllerPrivate;
    
    class LIBGEO_EXPORT GeoController : public QObject
    {
        Q_OBJECT
    
    public:
        explicit GeoController(QObject *parent = nullptr);
        ~GeoController();
    
        void setOrt(const QString&);
        void setHome(const QGeoCoordinate&);
        void doSearch(void);
        uint getCnt(void);
        QList<data::GeoPoint*> getPointList(void);
    
        bool Error;
    
    protected:
        QScopedPointer<GeoControllerPrivate> const d_ptr;
    
    private:
        Q_DECLARE_PRIVATE(GeoController);
    };
    
    }
    
    #endif // GEO_H
    

    source.cpp:

    #include "controller/geo-controller.h"
    
    namespace geo {
    
    class GeoControllerPrivate
    {
        Q_DISABLE_COPY(GeoControllerPrivate)
        Q_DECLARE_PUBLIC(GeoController)
    
    public:
        GeoControllerPrivate(GeoController *parent)
            : q_ptr(parent)
            , qGeoService("osm")
        {
             pQGeoCoder = qGeoService.geocodingManager();
             // qDebug() << ">> GeoController > Constructor (" << __LINE__ << ") error " << qGeoService.error() << ", errorstring " << qGeoService.errorString() << ", geocoding " << qGeoService.geocodingFeatures();
             if (!pQGeoCoder) {
                 // qDebug() << ">> GeoController > Constructor (" << __LINE__ << ") GeoCodingManager not available!";
                 Error = true;
             }
        }
    
        bool doSearch(void)
        {
            Error = false;
    
            qGeoCodeReply = pQGeoCoder->geocode(qGeoAddress);
            if (!qGeoCodeReply) {
                // qDebug() << ">> GeoController > doSearch (" << __LINE__ << ") search failed!";
                Error = true;
            }
            // else
                // qDebug() << ">> GeoController > doSearch (" << __LINE__ << ") start search";
    
            return Error;
        }
    
        GeoController           *const q_ptr;
        QGeoServiceProvider     qGeoService;
        QGeoCodingManager       *pQGeoCoder{nullptr};
        QGeoAddress             qGeoAddress;
        QGeoCodeReply           *qGeoCodeReply{nullptr};
        QString                 Ort;
        uint                    CntResult;
        QGeoCoordinate          Home;
        bool                    Error;
        QList<data::GeoPoint*>  PointList;
    
    public slots:
        void searchDone(void)
        {
            QList<QGeoLocation> Loc = qGeoCodeReply->locations();
            CntResult               = Loc.count();
            QGeoCoordinate      Temp;
            data::GeoPoint      Point;
    
            PointList.clear();
    
            // qDebug() << ">> GeoController > searchDone (" << __LINE__ << ") " << qGeoCodeReply->isFinished();
            // qDebug() << ">> GeoController > searchDone (" << __LINE__ << ") " << Loc.count();
    
            for (uint i=0; i < CntResult; ++i) {
                Temp = Loc.at(i).coordinate();
                Point.Clear();
                Point.setPoint(Temp);
                if (Home.isValid()) {
                    Point.setAzimut(Temp.azimuthTo(Home));
                    Point.setDistance(Temp.distanceTo(Home) / 1000);
                }
                PointList.append(&Point);
                // qDebug() << ">> GeoController > searchDone (" << __LINE__ << ") " << Temp << ", distance = " << Point.getDistance() << ", Azimut = " << Point.getAzimut();
            }
        }
    };
    
    GeoController::GeoController(QObject *parent)
        : QObject(parent)
        , d_ptr(new GeoControllerPrivate(this))
    {
    }
    
    GeoController::~GeoController()
    {
    }
    
    void GeoController::doSearch(void)
    {
        Q_D(GeoController);
        // qDebug() << ">> GeoController > doSearch (" << __LINE__ << ") suche nach Ort = " << d_ptr->Ort;
        Error = d->doSearch();
        connect(d->qGeoCodeReply, SIGNAL(finished()), this, SLOT(searchDone()));
    }
    
    }
    

    Könnte mir einer ein Tip geben, wie ich das mit den Signal / Slot in einer privaten Klasse mache.
    Danke schon mal.
    gruss
    martin



  • Ok. Wenn ich in bool doSearch (void) folgendes schreibe

           QObject::connect(qGeoCodeReply, &QGeoCodeReply::finished, [=](){searchDone();});
    

    Funktioniert es. Allerdings habe ich keine Ahnung was ich da gemacht habe. (Lambda Funktionen sagen mir nichts).

    Habt Ihr eine Erklärung dafür?

    gruss
    martin


  • Moderators

    @msauer751 said in Frage zum Thema private implementation:

    Habt Ihr eine Erklärung dafür?

    sicher,
    in deiner Geo klasse hast du keine deiner Funktionen als SLOT definiert, Qt hat auch hierfür Makros.

    deswegen die Meldung

    Object::connect: No such slot geo::GeoController::searchDone() in ../../lib-geo/source/controller/geo-controller.cpp:108.
    

    searchDone ist nur eine normale Klassenfunktion, aber die alte Qt4 Syntax brauch die Makros.
    Die neue Qt5 Syntax braucht diese nicht.
    Du bist den extra (und hier unnötigen) Schritt gegangen und hast Qt5Connect Syntax mit einem Lambda verknüpft.

    Lass es mich für dich vereinfachen:

    Object::connect(qGeoCodeReply, &QGeoCodeReply::finished, this, &GeoController::searchDone);
    


  • @J-Hilk

    Der SLOT befindet sich als public slots (void searchDone(void)) in GeoCodePrivate.
    Da GeoCodePrivate nicht von QObject abgeleitet ist, kann ich in der Funktion doSearch auch kein Connect aufrufen. Deswegen habe ich das connect in der Klasse GeoCode mit den Lambda ausdrücken.

    gruss

    martin


Log in to reply