Statische Template Methoden - undefinied reference to - Fehler



  • GPC schrieb:

    Sagt dir der Begriff "Singleton" was? Falls nicht, wir haben einen Artikel im Magazin dazu: http://www.c-plusplus.net/forum/viewtopic-var-t-is-155350.html (Punkt 4.2)

    Stimmt, Singleton wäre die Alternative.

    @Simon2:
    Aber ich will nicht meine Hilfsmethoden preisgeben (soweit kommt's noch! ^^)
    Außerdem heißt es nicht umsonst objekt-orientiertes Programmieren 😛

    Also ich hab's jetzt modifiziert:

    #ifndef QuickSort2_Header
    #define QuickSort2_Header
    
    #include <iostream>
    #include <stdlib.h>
    #include <time.h>
    #include "Comparable.hpp"
    #include "tools.hpp"
    
    using namespace Toolkit;
    
    class QuickSort {
    
        public:
        static QuickSort& Instance() {
            static QuickSort instance;
            return instance;
        }
    
        template <class T>
        void quicksort(Comparable<T>**& a, int length) {
            quicksort(a,0,length-1);
        }
    
        private:
        QuickSort() {}
    
        bool randomized;
    
        template <class T>
        void quicksort(Comparable<T>**& a, int l, int r) {
            int p = getPivot(l,r);
            Comparable<T>* pivot = a[p];
    
            int i = l,
                j = r;
    
            while (true) {
                while (a[i]->compareTo(pivot->getValue()) <= 0) i++;
                while (pivot->compareTo(a[j]->getValue()) > 0) j--;
                if (i>=j) break;
                swap(a, i, j);
            }
    
            swap(a, i, pivot-*a);
    
            quicksort(a, l, i-1);
            quicksort(a, i+1,r);
        }
    
        int getPivot(int l, int r) {
            return getRandomPivot(l,r);
        }
    
        int getRandomPivot(int l, int r) {
            if (!randomized) {
                srand(time(NULL));
                randomized = true;
            }
    
            return l+rand()%abs(r-l+1);
        }
    
        protected:
        QuickSort(const QuickSort& other) { }
    };
    
    inline QuickSort& getQuickSortInstance() {
        return QuickSort::Instance();
    }    
    #endif
    

    Allerdings gibts jetzt unschöne Runtime-Fehler (Programmabsturz). Ich versuche mich mal da alleine durchzuwursteln, aber wenn jemand spontan einen Fehler sieht, kann er ihn gerne posten! 🙂

    Gruß
    Redfrettchen



  • Redfrettchen schrieb:

    @Simon2:
    Aber ich will nicht meine Hilfsmethoden preisgeben (soweit kommt's noch! ^^)
    Außerdem heißt es nicht umsonst objekt-orientiertes Programmieren 😛

    DArum kommst du aber nicht herum (zumindest solange du template-basiert arbeitest ;))

    Ansonsten:
    > Auch wenn dir manche Leute das Gegenteil erzählen, C++ ist keine (reine) OOP-Sprache, sondern eine Hybridsprache. Und in vielen Fällen (deine Anwendung gehört dazu) ist es sinnvoller, davon Gebrauch zu machen und imperativ zu arbeiten. Wenn du OOP in Reinform erleben willst, bleib bei Java.

    > Die Referenzen auf einen doppelten Pointer, die du übergibst sehen mir etwas übertrieben aus. Bist du sicher, daß die (a) nötig sind und (b) korrekt übergeben wurden?



  • Redfrettchen schrieb:

    GPC schrieb:

    Sagt dir der Begriff "Singleton" was? Falls nicht, wir haben einen Artikel im Magazin dazu: http://www.c-plusplus.net/forum/viewtopic-var-t-is-155350.html (Punkt 4.2)

    Stimmt, Singleton wäre die Alternative.

    @Simon2:
    Aber ich will nicht meine Hilfsmethoden preisgeben (soweit kommt's noch! ^^)
    Außerdem heißt es nicht umsonst objekt-orientiertes Programmieren :P...

    Ähhh - das heißt, Du entscheidest Dich für ein inkonsistentes Programmdesign, damit "OOP" drübersteht ?
    BTW: Was Du da tust ist und bleibt aber einfach NICHT-OO - das kannst Du in eine class kleiden, wie Du willst - einfach weil die Aufgabenstellung eine reinen Algorithmus beschreibt, der überhaupt nichts Objektorientiertes aufweist (Was sind denn die Objekte, die jeweils einen Lebenszyklus und eine Identität haben und miteinander kommunizieren ?). Das ist auch in Java nicht anders, auch wenn die Sprache einen zwingt, jede Funktion in eine class zu stecken....

    Und zum Thema "Sourcen verstecken": Das gibst Du in dem Augenblick auf, in dem Du templates nutzt - egal, ob in einer class oder nicht.
    (Alternativ kannst Du natürlich den Comeau-Compiler und sein "export-Feature" nehmen ... damit begrenzt Du aber die Zahl Deiner Nutzer drastisch).

    Gruß,

    Simon2.



  • CStoll schrieb:

    DArum kommst du aber nicht herum (zumindest solange du template-basiert arbeitest ;))

    Simon2 schrieb:

    Und zum Thema "Sourcen verstecken": Das gibst Du in dem Augenblick auf, in dem Du templates nutzt - egal, ob in einer class oder nicht.
    (Alternativ kannst Du natürlich den Comeau-Compiler und sein "export-Feature" nehmen ... damit begrenzt Du aber die Zahl Deiner Nutzer drastisch).

    Soweit ich das jetzt gesehen habe, kann ich keine privaten Template-Methoden eines Objekts aufrufen (jedenfalls bekomme ich dann einen Compilerfehler). Oder meint ihr etwas anderes?

    CStoll schrieb:

    Ansonsten:
    > Auch wenn dir manche Leute das Gegenteil erzählen, C++ ist keine (reine) OOP-Sprache, sondern eine Hybridsprache. Und in vielen Fällen (deine Anwendung gehört dazu) ist es sinnvoller, davon Gebrauch zu machen und imperativ zu arbeiten. Wenn du OOP in Reinform erleben willst, bleib bei Java.

    In C++ wie auch in Java arbeitet man doch sowieso imperativ (manchmal vielleicht auch ein wenig funktional). Doch man sollte nicht gezwungen sein, prozedural zu programmieren, bzw. das mit OOP zu verquicken, meiner Meinung nach. Natürlich ist auch Java nicht konsequent objektorientiert, aber vergleichsweise "objektorientierter".
    Vielleicht widerspricht meine Vorstellung vom Programmieren auch der Philosophie von C++... ka ^^

    Simon2 schrieb:

    Ähhh - das heißt, Du entscheidest Dich für ein inkonsistentes Programmdesign, damit "OOP" drübersteht ?
    BTW: Was Du da tust ist und bleibt aber einfach NICHT-OO - das kannst Du in eine class kleiden, wie Du willst - einfach weil die Aufgabenstellung eine reinen Algorithmus beschreibt, der überhaupt nichts Objektorientiertes aufweist (Was sind denn die Objekte, die jeweils einen Lebenszyklus und eine Identität haben und miteinander kommunizieren ?). Das ist auch in Java nicht anders, auch wenn die Sprache einen zwingt, jede Funktion in eine class zu stecken....

    Damit verweigerst du aber dem Singleton-Pattern die Existenz als objektorientiertes Entwurfsmuster...

    CStoll schrieb:

    > Die Referenzen auf einen doppelten Pointer, die du übergibst sehen mir etwas übertrieben aus. Bist du sicher, daß die (a) nötig sind und (b) korrekt übergeben wurden?

    Ich wollte eigentlich ein Array von Objekten haben. Aber das kann ich nicht anlegen, wenn der Konstruktor der Klasse einen Parameter fordert. Konkret wollte ich ein Array von Objekten meiner eigenen Klasse Integer haben und es danach erst mit zufälligen int-Werten erzeugten Integer-Objekten befüllen. Das funktionierte leider nicht.
    Also hab ich jetzt ein Array von Zeigern auf meine Objekte, dass ich dann mit den Zeigern auf meine "zufälligen" Integer-Objekte fülle.
    Das ganze lässt sich bestimmt mit Containern eleganter lösen, aber da habe ich mich noch nicht so sehr beschäftigt, weshalb ich das ganze erstmal mit Arrays machen wollte.

    Hier ist die main.cpp:

    #include "Integer.hpp"
    #include "tools.hpp"
    #include "quicksort.hpp"
    #include <iostream>
    
    using namespace std;
    
    int main() {
    
        int length = 5;
        int* array = Toolkit::getRandomArray(length,5);
    
        Integer* a[length];
        for (int i=0; i<length; i++) {
            a[i] = new Integer(array[i]);
        }
    
        for (int i=0; i<length; i++) {
            cout<<a[i]->getIntegerValue()<<" ";
        }
    
        Comparable<Integer>** c = (Comparable<Integer>**)a;
    
        QuickSort::Instance().quicksort(c, length);
    cout<<"blub?";
        for (int i=0; i<length; i++) {
            cout<<a[i]->getIntegerValue()<<" ";
        }
    
        delete [] array;
    
        Toolkit::wait();
    
        return 0;
    }
    

    getRandomArray(int,int) gibt mir ein int-Array gewünschter Größe mit zufälligen Werten aus einem bestimmten Bereich zurück. Habe ich das Array am Ende wieder richtig gelöscht (es wurde in der Hilfsmethode mit new erzeugt)? Woher weiß delete, wie groß mein Array ist?

    a ist nun mein Array von Zeigern auf meine Objekte. Blöderweise muss ich nun noch eine Hilfsvariable anlegen und das ganze gewaltsam casten, damit ich es quicksort übergeben kann. Wahrscheinlich liegt hier der Fehler (denn "blub?" wird nicht ausgegeben), aber ich kenne keine Alternative.



  • Redfrettchen schrieb:

    CStoll schrieb:

    DArum kommst du aber nicht herum (zumindest solange du template-basiert arbeitest ;))

    Simon2 schrieb:

    Und zum Thema "Sourcen verstecken": Das gibst Du in dem Augenblick auf, in dem Du templates nutzt - egal, ob in einer class oder nicht.
    (Alternativ kannst Du natürlich den Comeau-Compiler und sein "export-Feature" nehmen ... damit begrenzt Du aber die Zahl Deiner Nutzer drastisch).

    Soweit ich das jetzt gesehen habe, kann ich keine privaten Template-Methoden eines Objekts aufrufen (jedenfalls bekomme ich dann einen Compilerfehler). Oder meint ihr etwas anderes?

    In erster Linie kann jeder deinen Code sehen - und mit einigen Tricksereien auch aufrufen 😉

    CStoll schrieb:

    Ansonsten:
    > Auch wenn dir manche Leute das Gegenteil erzählen, C++ ist keine (reine) OOP-Sprache, sondern eine Hybridsprache. Und in vielen Fällen (deine Anwendung gehört dazu) ist es sinnvoller, davon Gebrauch zu machen und imperativ zu arbeiten. Wenn du OOP in Reinform erleben willst, bleib bei Java.

    In C++ wie auch in Java arbeitet man doch sowieso imperativ (manchmal vielleicht auch ein wenig funktional). Doch man sollte nicht gezwungen sein, prozedural zu programmieren, bzw. das mit OOP zu verquicken, meiner Meinung nach. Natürlich ist auch Java nicht konsequent objektorientiert, aber vergleichsweise "objektorientierter".
    Vielleicht widerspricht meine Vorstellung vom Programmieren auch der Philosophie von C++... ka ^^

    Deine Vorstellung von Programmieren widerspricht fast jeder bekannten Philosophie 😉 Das ist der gewaltsame Versuch, etwas in ein Objekt zu pressen, nur damit du behaupten kannst, objektorientiert zu arbeiten.

    Simon2 schrieb:

    Ähhh - das heißt, Du entscheidest Dich für ein inkonsistentes Programmdesign, damit "OOP" drübersteht ?
    BTW: Was Du da tust ist und bleibt aber einfach NICHT-OO - das kannst Du in eine class kleiden, wie Du willst - einfach weil die Aufgabenstellung eine reinen Algorithmus beschreibt, der überhaupt nichts Objektorientiertes aufweist (Was sind denn die Objekte, die jeweils einen Lebenszyklus und eine Identität haben und miteinander kommunizieren ?). Das ist auch in Java nicht anders, auch wenn die Sprache einen zwingt, jede Funktion in eine class zu stecken....

    Damit verweigerst du aber dem Singleton-Pattern die Existenz als objektorientiertes Entwurfsmuster...

    Singleton als solches ist objektorientiert. Es wird ein Objekt angelegt, das zentral für alle als Ansprechpartner fungiert. Aber was du da machst, ist nicht mehr objektorientiert (OO bedeutet mehr, als nur ein "class xyz{...}" um deinen Code zu bauen ;)).

    CStoll schrieb:

    > Die Referenzen auf einen doppelten Pointer, die du übergibst sehen mir etwas übertrieben aus. Bist du sicher, daß die (a) nötig sind und (b) korrekt übergeben wurden?

    Ich wollte eigentlich ein Array von Objekten haben. Aber das kann ich nicht anlegen, wenn der Konstruktor der Klasse einen Parameter fordert. Konkret wollte ich ein Array von Objekten meiner eigenen Klasse Integer haben und es danach erst mit zufälligen int-Werten erzeugten Integer-Objekten befüllen. Das funktionierte leider nicht.
    Also hab ich jetzt ein Array von Zeigern auf meine Objekte, dass ich dann mit den Zeigern auf meine "zufälligen" Integer-Objekte fülle.
    Das ganze lässt sich bestimmt mit Containern eleganter lösen, aber da habe ich mich noch nicht so sehr beschäftigt, weshalb ich das ganze erstmal mit Arrays machen wollte.

    Statt zu versuchen, an deinem Unverständnis vorbeizuarbeiten, solltest du die Probleme beim Schopf packen 😉 Und als Ansatz könntest du dir mal den Weg ansehen, den die STL gegangen ist - ein Algorithmus bekommt Iteratoren mitgegeben, die den gültigen Bereich umschließen. Und der Cast zwischen verschiedenen Zeigertypen erzeugt im Endeffekt undefiniertes Verhalten - wenn du mit der "normalen" Sortierreihenfolge nicht zufrieden bist, solltest du die Vergleichsfunktion gesondert übergeben (nimm std::sort() als Vorbild ;)).



  • CStoll schrieb:

    Redfrettchen schrieb:

    Soweit ich das jetzt gesehen habe, kann ich keine privaten Template-Methoden eines Objekts aufrufen (jedenfalls bekomme ich dann einen Compilerfehler). Oder meint ihr etwas anderes?

    In erster Linie kann jeder deinen Code sehen - und mit einigen Tricksereien auch aufrufen 😉

    Man kann alles irgendwie aufrufen (würde ich mal behaupten), aber formal ist wenigstens die Intention zu erkennen, dass man diese Methoden nicht von außen aufrufen soll.

    CStoll schrieb:

    Redfrettchen schrieb:

    In C++ wie auch in Java arbeitet man doch sowieso imperativ (manchmal vielleicht auch ein wenig funktional). Doch man sollte nicht gezwungen sein, prozedural zu programmieren, bzw. das mit OOP zu verquicken, meiner Meinung nach. Natürlich ist auch Java nicht konsequent objektorientiert, aber vergleichsweise "objektorientierter".
    Vielleicht widerspricht meine Vorstellung vom Programmieren auch der Philosophie von C++... ka ^^

    Deine Vorstellung von Programmieren widerspricht fast jeder bekannten Philosophie 😉 Das ist der gewaltsame Versuch, etwas in ein Objekt zu pressen, nur damit du behaupten kannst, objektorientiert zu arbeiten.

    Objektorientiert Programmieren heißt nun mal, alles in Objekte zu verwandeln. Zugegeben ist mein "Einstiegsprojekt" kein Musterbeispiel, aber es ging mir ja erstmal nur darum, überhaupt meine Ziele zu erreichen:
    - ein Array von vergleichbaren Objekten mit Quicksort zu sortieren.
    - die benutzten Hilfsmethoden nicht öffentlich zugänglich zu machen.

    Wenn ich mir also eine generische Comparable-Array Klasse baue, die als Methode quicksort hat, dann sag mir noch jemand, dass das nicht objektorientiert ist.

    CStoll schrieb:

    Redfrettchen schrieb:

    Ich wollte eigentlich ein Array von Objekten haben. Aber das kann ich nicht anlegen, wenn der Konstruktor der Klasse einen Parameter fordert. Konkret wollte ich ein Array von Objekten meiner eigenen Klasse Integer haben und es danach erst mit zufälligen int-Werten erzeugten Integer-Objekten befüllen. Das funktionierte leider nicht.
    Also hab ich jetzt ein Array von Zeigern auf meine Objekte, dass ich dann mit den Zeigern auf meine "zufälligen" Integer-Objekte fülle.
    Das ganze lässt sich bestimmt mit Containern eleganter lösen, aber da habe ich mich noch nicht so sehr beschäftigt, weshalb ich das ganze erstmal mit Arrays machen wollte.

    Statt zu versuchen, an deinem Unverständnis vorbeizuarbeiten, solltest du die Probleme beim Schopf packen 😉 Und als Ansatz könntest du dir mal den Weg ansehen, den die STL gegangen ist - ein Algorithmus bekommt Iteratoren mitgegeben, die den gültigen Bereich umschließen. Und der Cast zwischen verschiedenen Zeigertypen erzeugt im Endeffekt undefiniertes Verhalten - wenn du mit der "normalen" Sortierreihenfolge nicht zufrieden bist, solltest du die Vergleichsfunktion gesondert übergeben (nimm std::sort() als Vorbild ;)).

    Ich verweigere mich ja nicht von vornherein der STL, aber was ich erreichen will (s.o.) muss auch so funktionieren.
    Wenn ich nun aber ein Array von Objekten übergeben will, wobei die Objekte des Arrays vom geforderten Typ erben, wie mache ich das?



  • Redfrettchen schrieb:

    Objektorientiert Programmieren heißt nun mal, alles in Objekte zu verwandeln.

    Und das ist genau der typische Denkfehler. "Objektorientiert" heißt nicht nur, alles in Objekte zu packen. "Objektorientiert" heißt auch, daß diese Objekte miteinander interagieren sollen.

    Wenn ich mir also eine generische Comparable-Array Klasse baue, die als Methode quicksort hat, dann sag mir noch jemand, dass das nicht objektorientiert ist.

    Wenn du dir eine generische Array-Klasse baust, spricht ja nichts dagegen. Aber dann sollte die Sortierung entweder in diese Klasse integriert werden oder du legst dir eine "normale" Funktion an, die sich darum kümmert. Eine eigene Klasse, die nur die Aufgabe "Sortieren" kapselt, ist sinnlos.

    CStoll schrieb:

    Redfrettchen schrieb:

    Ich wollte eigentlich ein Array von Objekten haben. Aber das kann ich nicht anlegen, wenn der Konstruktor der Klasse einen Parameter fordert. Konkret wollte ich ein Array von Objekten meiner eigenen Klasse Integer haben und es danach erst mit zufälligen int-Werten erzeugten Integer-Objekten befüllen. Das funktionierte leider nicht.
    Also hab ich jetzt ein Array von Zeigern auf meine Objekte, dass ich dann mit den Zeigern auf meine "zufälligen" Integer-Objekte fülle.
    Das ganze lässt sich bestimmt mit Containern eleganter lösen, aber da habe ich mich noch nicht so sehr beschäftigt, weshalb ich das ganze erstmal mit Arrays machen wollte.

    Statt zu versuchen, an deinem Unverständnis vorbeizuarbeiten, solltest du die Probleme beim Schopf packen 😉 Und als Ansatz könntest du dir mal den Weg ansehen, den die STL gegangen ist - ein Algorithmus bekommt Iteratoren mitgegeben, die den gültigen Bereich umschließen. Und der Cast zwischen verschiedenen Zeigertypen erzeugt im Endeffekt undefiniertes Verhalten - wenn du mit der "normalen" Sortierreihenfolge nicht zufrieden bist, solltest du die Vergleichsfunktion gesondert übergeben (nimm std::sort() als Vorbild ;)).

    Ich verweigere mich ja nicht von vornherein der STL, aber was ich erreichen will (s.o.) muss auch so funktionieren.

    Ich sagt nicht, daß du unbedingt std::sort() verwenden solltest (für den Lerneffekt ist es durchaus praktisch, eigene Wege zu gehen). Aber für einen generischen Ansatz kannst du die STL schonmal als Empfehlung für eine brauchbare Schnittstelle verwenden.

    Wenn ich nun aber ein Array von Objekten übergeben will, wobei die Objekte des Arrays vom geforderten Typ erben, wie mache ich das?

    Wenn du sowieso die Vererbungsbeziehung nutzen willst, kannst du auch über Polymorphie arbeiten (aber beschwer dich dann nicht, wenn jemand Äpfel mit Birnen vergleichen will) - da brauchst du keine Templates.
    Templates benötigen keine Vererbungsbeziehungen zu irgendwas, um funktionieren zu können. Alles was ein Template verlangt ist, daß die verwendeten Typen die Operationen unterstützen, die verwendet werden.

    In deinem Beispiel könntest du einfach einen Zeiger auf ein T und eine Längenangabe übergeben und schon kannst du alles sortieren, was op< implementiert hat. Wenn du noch eine zusätzliche Vergleichsfunktion übergibst, kannst du alles sortieren, was du irgendwie vergleichen kannst:

    template<typename T>
    void quicksort(T* data,size_t len)
    { quicksort(data,0,len-1,std::less<T>()); }
    //less<> ist ein Funktor, der den <-Operator wrappt
    
    template<typename T,typename F>
    void quicksort(T* data,size_t len,F pred)
    { quicksort(data,0,len-1,pred); }
    //das schluckt als letzten Parameter eine (fast beliebige) zweistellige Funktion
    
    template<typename T,typename F>
    void quicksort(T* data,size_t l,size_t r,F pred)
    {
      ...
      /*hier kannst du anstelle von CompareTo(..) das übergebene Prädikat verwenden,
        um zwei Werte zu vergleichen
    
        Vorraussetzungen:
        * pred kann mit zwei Parametern vom Typ T aufgerufen werden
          (entweder die Parameter sind T, const T& oder etwas, in das T konvertiert werden kann)
        * pred liefert eine bool-wertige Rückgabe (bzw. konvertierbar nach bool)
    
        * pred erzeugt eine Halbordnung (d.h. pred(x,y) liefert true gdw x "kleiner" als y ist)
      */
    }
    

    Wenn du damit glücklich bist, kannst du das in eine Klasse packen. Aber notwendig ist das nicht, um ein Array sortieren zu können.



  • CStoll schrieb:

    Redfrettchen schrieb:

    Objektorientiert Programmieren heißt nun mal, alles in Objekte zu verwandeln.

    Und das ist genau der typische Denkfehler. "Objektorientiert" heißt nicht nur, alles in Objekte zu packen. "Objektorientiert" heißt auch, daß diese Objekte miteinander interagieren sollen.

    Wenn ich mir also eine generische Comparable-Array Klasse baue, die als Methode quicksort hat, dann sag mir noch jemand, dass das nicht objektorientiert ist.

    Wenn du dir eine generische Array-Klasse baust, spricht ja nichts dagegen. Aber dann sollte die Sortierung entweder in diese Klasse integriert werden oder du legst dir eine "normale" Funktion an, die sich darum kümmert. Eine eigene Klasse, die nur die Aufgabe "Sortieren" kapselt, ist sinnlos.

    Im Kontext der Möglichkeiten akzeptiert.

    CStoll schrieb:

    Wenn ich nun aber ein Array von Objekten übergeben will, wobei die Objekte des Arrays vom geforderten Typ erben, wie mache ich das?

    Wenn du sowieso die Vererbungsbeziehung nutzen willst, kannst du auch über Polymorphie arbeiten (aber beschwer dich dann nicht, wenn jemand Äpfel mit Birnen vergleichen will) - da brauchst du keine Templates.
    Templates benötigen keine Vererbungsbeziehungen zu irgendwas, um funktionieren zu können. Alles was ein Template verlangt ist, daß die verwendeten Typen die Operationen unterstützen, die verwendet werden.

    Aber ich habe doch gar keine Schnittstelle! Woher soll ich wissen, welche Anforderungen eine Template-Methode an meinen Typ stellt?

    CStoll schrieb:

    In deinem Beispiel könntest du einfach einen Zeiger auf ein T und eine Längenangabe übergeben und schon kannst du alles sortieren, was op< implementiert hat. Wenn du noch eine zusätzliche Vergleichsfunktion übergibst, kannst du alles sortieren, was du irgendwie vergleichen kannst: (code)

    Woher soll denn nun jemand allein durch den Code wissen, was F sein soll?
    Einen Comparator zu übergeben, ist ja wieder eine ganz andere Geschichte, die schon eher wieder in Richtung funktionale Programmierung zeigt. Mir geht es um die objektorientierte Behandlung von abstrakten Datentypen. Wenn also eine Menge vergleichbar sein soll, dann muss der ADT dieser "Algebra" eine Vergleichsoperation zur Verfügung stellen.



  • Redfrettchen schrieb:

    ...
    Damit verweigerst du aber dem Singleton-Pattern die Existenz als objektorientiertes Entwurfsmuster...

    KEINESWEGS !!
    Wie kommst Du darauf ?
    Ein Singletonobjekt ist ein Objekt im klassischen Sinne: Es hat Identität/Status, kommmuniziert mit anderen Objekten, ... ich wiß echt nicht, woher Du Deine Aussage nimmst. 😕

    Redfrettchen schrieb:

    ...
    Soweit ich das jetzt gesehen habe, kann ich keine privaten Template-Methoden eines Objekts aufrufen (jedenfalls bekomme ich dann einen Compilerfehler). Oder meint ihr etwas anderes?...

    Also ich hatte unter Folgendem:

    Redfrettchen schrieb:

    ...
    Aber ich will nicht meine Hilfsmethoden preisgeben (soweit kommt's noch! ^^)
    ...

    verstanden, dass Du nicht möchtest, dass die Sourcen einsehbar sind.

    Wenn es Dir nur um die "Unaufrufbarkeit" geht, sehe ich nicht, wo Du die bräuchtest. Die Kapselung mit private/protected/public dient dazu, die Konsistenz des Zustands eines Objekts sicherzustellen - da Du aber keinen "inneren Zustand" hast, gibt es da nix zu schüzen.

    Willst Du Klarheit Deiner Schnittstelle, genügt es, Deine Hilfsfunktionen in einer "quicksort.impl.hpp" auszulagern (vllt. zusätzlich noch in einem anonymen namespace, dann bekommt man nicht mal Kollisionsprobleme).

    Gruß,

    Simon2.



  • Redfrettchen schrieb:

    CStoll schrieb:

    Wenn ich nun aber ein Array von Objekten übergeben will, wobei die Objekte des Arrays vom geforderten Typ erben, wie mache ich das?

    Wenn du sowieso die Vererbungsbeziehung nutzen willst, kannst du auch über Polymorphie arbeiten (aber beschwer dich dann nicht, wenn jemand Äpfel mit Birnen vergleichen will) - da brauchst du keine Templates.
    Templates benötigen keine Vererbungsbeziehungen zu irgendwas, um funktionieren zu können. Alles was ein Template verlangt ist, daß die verwendeten Typen die Operationen unterstützen, die verwendet werden.

    Aber ich habe doch gar keine Schnittstelle! Woher soll ich wissen, welche Anforderungen eine Template-Methode an meinen Typ stellt?

    In Kurzfassung: Indem du dir ansiehst, was die Funktion mit deinen Daten machen will (genau das macht auch der Compiler, wenn er das Template instanziiert - er prüft nach, ob alle verwendeten Operationen für den aktuellen Typ verfügbar sind und setzt sie entsprechend ein (oder meldet dir einen Fehler ala "no matching call for...").

    CStoll schrieb:

    In deinem Beispiel könntest du einfach einen Zeiger auf ein T und eine Längenangabe übergeben und schon kannst du alles sortieren, was op< implementiert hat. Wenn du noch eine zusätzliche Vergleichsfunktion übergibst, kannst du alles sortieren, was du irgendwie vergleichen kannst: (code)

    Woher soll denn nun jemand allein durch den Code wissen, was F sein soll?

    Durch den Code wird er auf jeden Fall erkennen, daß es eine zweistellige "Funktion" sein soll, die zwei T's schlucken und einen bool zurückgeben soll. Die Anforderung bezüglich der Halbordnung mußt du dann vermutlich dokumentieren (und darauf hoffen, daß dich niemand nach equals<int> sortieren lassen will).

    Einen Comparator zu übergeben, ist ja wieder eine ganz andere Geschichte, die schon eher wieder in Richtung funktionale Programmierung zeigt. Mir geht es um die objektorientierte Behandlung von abstrakten Datentypen. Wenn also eine Menge vergleichbar sein soll, dann muss der ADT dieser "Algebra" eine Vergleichsoperation zur Verfügung stellen.

    Klar kannst du die Teile mit dem "F pred" auch weglassen und einfach vorschreiben, daß die zu sortierenden Objekte nach Größe verglichen werden können. Und am einfachsten geht das, indem du einen operator< voraussetzt (den haben alle eingebauten Typen - und für deine eigenen Klassen kannst du ihn auch ohne größeren Aufwand erzeugen (das schwierigste daran ist die Frage, wann x "kleiner" als y sein soll)) und für Elementvergleich nutzt.



  • Redfrettchen schrieb:

    ...
    Aber ich habe doch gar keine Schnittstelle! Woher soll ich wissen, welche Anforderungen eine Template-Methode an meinen Typ stellt?...

    Das ist IMO eine Schwäche in C++: Man kann sowas nicht standardisiert dokumentieren !
    Letztlich bleibt einem nur, tatsächlich eine "Doku" zu schreiben oder, es jedem User zu überlassen es selbst herauszufinden.
    Noch dramatischer wird es IMO dadurch, dass ein Verstoß gegen die "Typanforderungen" entweder gar nicht (templates werden nur bei Nutzung istantiiert) auffallen oder derartig schräg vom Compiler gemeldet, dass man sehr alt aussieht.

    Bei anderen "Schnittstellenaussagen" wie const, private, ... wurde das (wie ich finde) sehr viel besser geregelt durch entsprechende keywords, deren Konsistenz der Compiler checken kann.

    Ich könnte mir einen Standard vorstellen, indem das bei der template-Deklaration angegeben wird,

    template <typename copy assign T1, typename pod T2, typename primitive T3, typename T4>
    class A {
    ...
    

    Aber da braucht man vermutlich eine sehr große Menge an "Typklassen" und entsprechenden keywords.

    Gruß,

    Simon2.



  • Genau, es wäre eben schön, wenn man an den generischen Typ explizite Anforderungen stellen könnte.
    Auch auf die Gefahr hin, eine unnötige Diskussion auszulösen:
    In Java kann man bei den generischen Typen eine Vererbungshierarchie fordern:

    public static <T extends Comparable<? super T>> void mergesort(T[] a) {
    //...
    }
    

    T muss also mit sich selbst oder einem Obertyp vergleichbar sein. Mit meiner Template-Klasse Comparable kann ich dieses Verhalten nur teilweise und auch mit ein wenig Trickserei nachahmen.
    Aber wahrscheinlich sollte man gar nicht erst versuchen, die generischen Typen von Java mit C++-Templates nachzuahmen...

    Unabhängig von Templates bleibt für mich aber noch das Problem, dass ich keine Arrays von Objekten anlegen kann, die durch einen parametrisierten Konstruktor erzeugt werden müssen. Wie macht man das? Oder macht man das gar nicht?



  • Redfrettchen schrieb:

    Genau, es wäre eben schön, wenn man an den generischen Typ explizite Anforderungen stellen könnte.
    Auch auf die Gefahr hin, eine unnötige Diskussion auszulösen:
    In Java kann man bei den generischen Typen eine Vererbungshierarchie fordern:

    public static <T extends Comparable<? super T>> void mergesort(T[] a) {
    //...
    }
    

    T muss also mit sich selbst oder einem Obertyp vergleichbar sein. Mit meiner Template-Klasse Comparable kann ich dieses Verhalten nur teilweise und auch mit ein wenig Trickserei nachahmen.
    Aber wahrscheinlich sollte man gar nicht erst versuchen, die generischen Typen von Java mit C++-Templates nachzuahmen...

    Ja, jede Sprache hat ihre Eigenheiten - und die Java Generics sind auch nicht unbedingt mit C++ Templates vergleichbar.

    Unabhängig von Templates bleibt für mich aber noch das Problem, dass ich keine Arrays von Objekten anlegen kann, die durch einen parametrisierten Konstruktor erzeugt werden müssen. Wie macht man das? Oder macht man das gar nicht?

    Dann nimmst du halt keine blanken Arrays (sind sowieso sehr fehleranfällig), sondern die C++ Alternative: vector<>.



  • Na gut, also auf in die STL! ^^

    Danke für die Unterstützung bis hier hin!


Anmelden zum Antworten