Anzeige:
Ergebnis 1 bis 5 von 5

Thema: Private Header und Klassen in C++ projekten

  1. #1
    Registrierter Benutzer
    Registriert seit
    31.12.2004
    Beiträge
    11

    Private Header und Klassen in C++ projekten

    Hallo,

    Ich befasse mich im Moment intensiv mit der sinnvollen Strukturierung von grösseren C++ Projekten. Bei Bibliotheken geht es ja immer wieder darum eine vernünftige Schnittstelle zu Verfügung zu stellen die sich nicht ändert und die eigentliche Implementierung vor dem Anwender zu verstecken. Ich bin in diversen Büchern (Stroustrup, Meyers) über ein paar Ansätze gestolpert die das erwähnen aber für mich nicht vollkommen verständlich erklären. In den Quellen von Qt4 und von der Coin3D-Bibliothek habe ich mittlerweile auch private header und klassen gefunden, verstehe aber noch nicht genau wie man das nun richtig umsetzt (die beiden Projekte sind zum "durchlesen" einfach zu gross).

    Frage: Kann mir jemand ein Tutorial nennen oder ein kurzes Beispiel geben wie das nun ganz konkret mit der Trennung von Interface und Implementierung aussieht? Hat jemand einen guten Literatur-Tipp auf Lager?

    Würde mir sehr weiter helfen.
    Danke schonmal.

  2. #2
    Registrierter Benutzer Avatar von BLUESCREEN3D
    Registriert seit
    08.11.2002
    Beiträge
    665
    Ich weiß nicht, was du mit "privaten Headern" meinst, aber die Trennung von Interface und Implementierung geht in der objektorientierten Programmierung üblicherweise so, dass du eine abstrakte Klasse (das Interface) hast, die die Signaturen aller wichtigen Methoden enthält.
    Die Implementierung ist dann eine andere Klasse, die von der Interface-Klasse abgeleitet ist (Vererbung).
    Das tolle daran ist, dass es mehrere Implementierungen geben kann und dass die einfach ausgetauscht oder dynamisch ausgewählt werden können, ohne viel im Programm zu ändern. Deshalb nutzt man dann im Programm auch die Interfaceklasse als Datentyp für Instanzen der Implementierungsklasse.

    Hier steht das nochmal mit Beispielen zu Jave: http://de.wikipedia.org/wiki/Schnitt...grammierung%29

    In C++ schreibt man Interfaces mit rein virtuellen Methoden (das sind die mit "= 0" am Ende).
    Das könnte dann z.B. so aussehen:
    Code:
    class ein_interface
    	{
    	public:
    		virtual ~ein_interface()
    			{
    			}
    
    		virtual void bla() = 0;
    	};
    
    class eine_implementierung : public ein_interface
    	{
    	public:
    		virtual void bla()
    			{
    			//inhalt
    			}
    	};
    
    int main()
    	{
    	//anwendung:
    
    	ein_interface *irgendwas = new eine_implementierung;
    
    	irgendwas->bla();
    
    	delete irgendwas;
    	}
    Literatur: Sowas sollte eig. in jedem Buch über Software Engineering stehen. Such ansonsten nach "interface", "program to an interface"
    Angewendet werden solche Trennungen z.B. ständig in dem Buch "Design Patterns", aber ich würde dir erstmal empfehlen, ein Buch über Software Engineering zu lesen.

    Da du auch vom Verstecken der Implementierung geredet hast: Das ganze nennt sich Information Hiding - siehe z.B. http://de.wikipedia.org/wiki/Datenka...grammierung%29 - und ist noch etwas anderes. Dabei könnte man in gewisser Weise aber die public-Methoden einer Klasse als "Interface" bezeichnen.

  3. #3
    Registrierter Benutzer
    Registriert seit
    31.12.2004
    Beiträge
    11
    Hallo und ganz herzlichen Dank für Deine Antwort!

    Genauso wie Du es beschreibst, habe ich das auch aufgefasst und in "Die C++ Programmiersprache" von Stroustrup (soweit ich weiss DIE BIBEL für C++-Programmierer :-) ) ist das auch ganz nett beschrieben. Nur fehlt mir dafür ein konkreteres Beispiel. Wie bereits oben beschrieben habe ich durchaus ein paar Beispiele in Qt finden können. Allerdings ist das Projekt mir dann wieder zu Gross um einen wirklich guten Überblick bekommen zu können.

    Ich hatte gehofft, das jemand ein paar ganz konkrete Beispiele in irgendwelchen kleineren Opensourche-Projekten o.ä. nennen könnte da ich das noch nicht ganz durchschaut hab bzw. ich es noch nicht so verinnerlicht habe das ich es im Schlaf implementieren könnte.

  4. #4
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Zitat Zitat von The_Student Beitrag anzeigen
    In den Quellen von Qt4 und von der Coin3D-Bibliothek habe ich mittlerweile auch private header und klassen gefunden, verstehe aber noch nicht genau wie man das nun richtig umsetzt (die beiden Projekte sind zum "durchlesen" einfach zu gross).
    Das nennt man auch "d-pointer" oder "pimpl" (pointer to implementation) Idiom.

    Die Grundidee ist, alle Datenmembers einer Klasse und Teile der Funktionalität an eine andere Klasse zu delegieren, deren Deklaration aber nur sehr lokal bekannt ist, eben "privat".

    Eingesetzt wird es hauptsächlich, um bei stabiler ABI (binäres Interface der Klasse) dennoch Erweiterungen machen zu können.

    Beispiel:
    Code:
    // Im Header
    class Point
    {
    public:
        Point();
    
        void setX(int x);
        void setY(int y);
    
        int x() const;
        int y() const;
    
    private:
        int m_x;
        int m_y;
    };
    
    // Im Source
    Point::Point() : m_x(0), m_y(0) {}
    
    void Point::setX(int x) { m_x = x; }
    void Point::setY(int y) { m_y = y; }
    
    int Point::x() const { return m_x; }
    int Point::y() const { return m_y; }
    Wenn du nun zu einem späteren Zeitpunkt gerne eine Methode hättest, mit der du Prüfen kannst, ob der Punkt "leer" ist oder echte Werte enhält, könntest du das so lösen
    Code:
    class Point
    {
    public:
        bool isValid() const;
    
    private:
        bool m_valid;
    };
    
    Point::Point() : m_x(0), m_y(0), m_valid(false) {}
    
    void Point::setX(int x) { m_x = x; m_valid = true; }
    void Point::setY(int y) { m_y = y; m_valid = true; }
    
    bool Point::isValid() const { return m_valid; }
    (Rest des Codes bleibt gleich)

    D.h. du brauchst eine neue Methode und einen neuen Member (m_valid)
    Das hinzufügen des Members ist aber nicht binärkompatibel, weil die Größe der Klasse anwächst.

    In einem d-pointer Szenario sieht die Ausgangsgrundlage so aus:
    Code:
    // Im Header
    class Point
    {
    public:
        Point();
        ~Point();
    
        void setX(int x);
        void setY(int y);
    
        int x() const;
        int y() const;
    
    private:
        class Private;
        Private* d;
    };
    
    // Im Source (oder in einem privaten Header)
    class Point::Private
    {
    public:
        Private() : m_x(0), m_y(0) {}
    
    public:
        int m_x;
        int m_y;
    };
    
    // Im Source
    Point::Point() : d(new Private()) {}
    Point::~Point() { delete d; }
    
    void Point::setX(int x) { d->m_x = x; }
    void Point::setY(int y) { d->m_y = y; }
    
    int Point::x() const { d->return m_x; }
    int Point::y() const { d->return m_y; }
    D.h. die Daten sind in der privaten Klasse Point::Private, die Klasse Point hat nur einen einzigen Member, nämlich einen Pointer auf die Instanz von Point::Private;

    Wie im ersten Beipiel jetzt die Änderung für Valid
    Code:
    class Point
    {
    public:
        bool isValid() const;
    };
    
    class Point::Private
    {
    public:
        Private() : m_x(0), m_y(0), m_valid(false) {}
    
    public:
        bool m_valid;
    };
    
    void Point::setX(int x) { d->m_x = x; d->m_valid = true; }
    void Point::setY(int y) { d->m_y = y; d->m_valid = true; }
    
    bool Point::isValid() const { return d->m_valid; }
    Die Klasse Point bekommt nur eine neue Methode. Da sie nicht virtuell ist, ist das binär kompatibel. Der neue Member geht in die nach außen unbekannte/unsichtbare Private Klasse.

    In diesem Fall ist es wichtig, dass man an ein paar Auswirkungen denkt:
    • man braucht einen Copy Constructor und Zuweisungsoperator oder muss sie deaktivieren
    • die Indirektion hat eine Runtime Overhead (Pointer muss dereferenziert werden)
    • auch wenn eine Point Instanz auf dem Stack angelegt wird, ihr Private Teil ist immer im Heap


    Für so eine primitive Klasse wie Point würde man das daher normalerweise nicht machen, aber bei komplexeren Aufgaben oder Abhängigkeiten kann die wesentliche Grundlage sein, eine Bibliothek länger (binär) stabil halten zu können.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  5. #5
    Registrierter Benutzer
    Registriert seit
    31.12.2004
    Beiträge
    11
    Herzlichen Dank für die Antwort! Das Prinzip war mir so klar, aber nicht soooo klar :-). Besonders das Argument mit der Binärkompatiblität kannte ich noch nicht. Jetzt muss ich mich nur noch entscheiden ob ich diese Methodik wirklich brauche oder nicht...

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •