Konsolen-Simulation des Algorithmus "Türme von Hanoi" unter Verwendung einer Klasse



  • SeppJ schrieb:

    Die Trennung von Logik und Darstellung ist eher ein technischer Aspekt. Man möchte die Darstellung abkoppeln, damit sie austauschbar wird. So kann die gleiche Programmlogik auf allerlei Art dargestellt werden. Dann wäre es ein leichtes, mein Progrämmchen mit einer anspruchsvollen 3D-Grafik auszurüsten, ohne an der Logik irgendetwas verändern zu müssen.
    ..
    Bei Werners Code? Die gehören mit zum Namen und haben keine syntaktische Bedeutung. Werner will dadurch unterscheidbar machen, ob eine Variable lokal ist oder ein Klassenmember ist.

    Achso, danke. Dann wäre man gut beraten, es so zu designern, dass die Windows-Programmierung später ohne Probleme anknüpfen kann 🙂 💡



  • osdt schrieb:

    Bist Du so freundlich uns zu erklären weshalb Du X(...) und Y(...) als globale Funktionen implementierst? Ich hab das hier schonmal irgendwo entdeckt und nur mit dem Kopf geschüttelt 😮 Du weißt schon dass es dafür Klassenmethoden gibt?

    Nun - sehe als persönlichen Stil meinerseits. Vom Standpunkt des Designs ist es IMHO völlig egal, ob Du in einer Klasse eine Methode oder eine (friend) Funktion, mit der Klasse als einer der Parameter implementierst. Technisch gibt es da Feinheiten - die kannst Du bei Scott Meyers nachlesen.
    In diesem speziellen Fall bei Positionen habe ich mir das so angewöhnt, und als ich es oben geschrieben habe, war das reine Gewohnheit ohne konkret darüber nach zu denken. Es hat nämlich den Vorteil, dass man sehr viele Strukturen als Positionen verwenden kann. Teilweise solche, wo man keine Methoden hinzufügen kann, weil es sich um sogenannten Legacy Code oder um eine Klasse aus dem Standard wie std::pair<> oder std::complex<> handelt.
    Man kann aber immer diese Funktionen X() und Y() implementieren. Das ist ein großer Vorteil, insbesondere bei der Template-Programmierung.

    osdt schrieb:

    Daraus resultiert dann völlig unleserlicher Code wie:

    for( int x = X(d.lower_left_); x <= X(d.upper_right_); ++x )
    

    .. erstaunlich! Denke Dir mal X(egal) heißt X-Wert-von-egal. Dann steht da:

    for( int x = X_Wert_von(d.lower_left_); x <= X_Wert_von(d.upper_right_); ++x )
    

    also: Schleife über alle x beginnend vom X-Wert der (unteren) linken Ecke bis zum X-Wert der (oberen) rechten Ecke.

    Gruß
    Werner



  • Hallo SeppJ,

    wenn dieses Forum eine Person wäre, müsste es wegen Schizophrenie sofort zum Psychiater. 😉

    SeppJ schrieb:

    .. die Objekte zeichnen sich selbst, das sollte nicht so sein. Logik und Darstellung sollten sauber getrennt sein, aber ich kenne die üblichen Designs dafür nicht und so wirklich zufrieden war ich auch nicht mit dem, was ich mir auf die Schnelle ausgedacht habe. Dein Design ist da ähnlich, wenn auch eleganter. Ist das schon eine der üblichen Vorgehensweisen? Da müsste man jetzt wohl wirklich mal einen Spieleprogrammierer fragen, wie das gut gemacht wird.

    ich hätte mir diese Diskussion - und ich begrüße sie sehr 👍 - eher in diesem Thread gewünscht, wo ich explizit danach fragte, wie man Code verständlich schreiben kann. Fünf von fünf die geantwortet haben, waren der Meinung, dass der Code des Springerproblems durch meinen (IMHO ojektorientierten) Ansatz nur komplexer, länger und weniger verständlich wird.
    Und dann poste ich hier ein Schnipsel im gleichen Stil und Du meinst:

    SeppJ schrieb:

    Logik und Darstellung sollten sauber getrennt sein

    😃 - also noch mehr Code und noch mehr Klassen und womöglich noch weniger verständlich?

    Ja - natürlich sollte man Logik und Darstellung sauber trennen - was denn sonst. In sogenannten 'professionellen' Programmen kenne ich das gar nicht mehr anders. Und das machen nicht nur Spieleprogrammierer so - Modell-View-Architekturen machen auch wo anders Sinn. Und sie machen auch Sinn, wenn man die View nicht austauschen will. Einfach um die Komplexität runter zu kriegen.

    Ob man das bei den Türmen von Hanoi macht oder nicht ist was anderes. Der Turm ist viel zu einfach, als dass das wirklich nenneswerte Verbesserungen des Programms nach sich zieht. Adererseits ist das 'ne nette Übung - und wenn man es mit dem Turm aus Hanoi nicht sauber hinbekommt, braucht man nicht zu hoffen, dass es bei großen/komplexen Objekten einfacher wird.

    ich melde mich zu dem Thema später noch mal.
    Gruß
    Werner



  • Ufff ... Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    http://s14.directupload.net/images/131208/5afa74r2.jpg

    Soweit sind die Klassen fertig designed u. programmiert. Es scheint auch zu funktionieren. Ob ich gravierende Verstöße gegen die objektorientierte Programmierung gemacht habe, weiß ich allerdings nicht:

    #include <windows.h>    // wegen Windows Handle für Konsolenposition
    #include <cstdlib>      // wegen system("cls")
    #include <sstream>      // wegen stringstream,  für int kombiniert mit char*
    #include <iostream>
    #include <deque>
    #include <string>
    using namespace std;
    
    class Slice {
        public:
            Slice();
            ~Slice();
    
            void setSlice(string slice);
            string getSlice();
            int getSliceCenter();
            int getSliceSize();        
    
        private:
            string m_slice_object;
            int m_slice_length;
            int m_slice_center;        
    };
    
    Slice::Slice()
    {    
    }
    
    Slice::~Slice()
    {    
    }
    
    //---------------------------------------------------------------------
    // gibt der Scheibe ein Aussehen
    //---------------------------------------------------------------------
    void Slice::setSlice(string slice) {
        m_slice_object = slice;
    }
    
    //---------------------------------------------------------------------
    // holt die Scheibe
    //---------------------------------------------------------------------
    string Slice::getSlice() {
        return m_slice_object;
    }
    
    //---------------------------------------------------------------------
    // bestimmt die Mitte der Scheibe
    //---------------------------------------------------------------------
    int Slice::getSliceCenter() {
        m_slice_center = (m_slice_object.length()+1)/2;
        return m_slice_center;
    }
    
    //---------------------------------------------------------------------
    // bestimmt die Größe der Scheibe
    //---------------------------------------------------------------------
    int Slice::getSliceSize() {
        m_slice_length = m_slice_object.size();
        return m_slice_length;
    }
    
    class Tower {        
        public:
            Tower();
            ~Tower();
    
            Slice getSlice(); //oberste Scheibe zurückgeben
            Slice getAt(const int &iter); // irgendwo eine Scheibe
            int getSize(); // Towergröße auslesen
            void addSlice(Slice slice);   // 1x aufnehmen    
            void removeSlice(); // 1x löschen      
    
        private:
            deque<Slice> m_tower_content;
    
    };
    Tower::Tower()
    {    
    }
    
    Tower::~Tower()
    {    
    }
    
    //---------------------------------------------------------------------
    // holt oberste Scheibe  
    //---------------------------------------------------------------------
    Slice Tower::getSlice() {
        return m_tower_content.at(0);
    }
    
    //---------------------------------------------------------------------
    // holt eine Scheibe an einer best. Stelle im Turm  
    //---------------------------------------------------------------------
    Slice Tower::getAt(const int &iter) {
        return m_tower_content.at(iter);
    }
    
    //---------------------------------------------------------------------
    // Scheibenzahl auslesen
    //---------------------------------------------------------------------
    int Tower::getSize() {
        return m_tower_content.size();
    }
    
    //---------------------------------------------------------------------
    // fügt eine Scheibe oben rein
    //---------------------------------------------------------------------
    void Tower::addSlice(Slice slice) {
        m_tower_content.push_front(slice);
    }
    
    //---------------------------------------------------------------------
    // löscht oberste Scheibe aus Turm
    //---------------------------------------------------------------------
    void Tower::removeSlice() {
        m_tower_content.pop_front();
    }
    
    class Display {
        public:
            Display();
            ~Display();
    
            void clr();
            void gotoXY(const int &line, const int &column);        
            void waitKeyboard() {getwchar();}          
            void print(const string &value);
            void print(const stringstream  &value);  
            void print(Tower &tower, int x_start, const int &y_start);
    };
    Display::Display()
    {
    }
    
    Display::~Display()
    {
    }
    
    //---------------------------------------------------------------------
    // // Bildschirm löschen
    //---------------------------------------------------------------------
    void Display::clr() {
        system("cls");
    }
    
    //---------------------------------------------------------------------
    // Positioniere Cursor auf Konsole
    //---------------------------------------------------------------------
    void Display::gotoXY(const int &line,
                         const int &column) {
    
        COORD p;
        p.Y = line;     // Zeile
        p.X = column;   // Spalte
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p);
    }
    
    //---------------------------------------------------------------------
    // Überladung 1: String auf dem Bildschirm
    //---------------------------------------------------------------------
    void Display::print(const string &value){
        cout << value << endl;
    }
    
    //---------------------------------------------------------------------
    // Überladung 2: StringStreams auf dem Bildschirm
    //---------------------------------------------------------------------
    void Display::print(const stringstream &value){
        cout << value.str() << endl;
    }
    
    //---------------------------------------------------------------------
    // Überladung 3: Tower auf Konsole ausgeben
    //---------------------------------------------------------------------
    void Display::print(Tower &tower,
                        int x_start,
                        const int &y_start) {
    
        int start_column; // Startspalte    
        for (int i=tower.getSize()-1; i > -1; i--) {    
            start_column = y_start - tower.getAt(i).getSliceCenter();
            this->gotoXY(x_start--, start_column);
            cout << tower.getAt(i).getSlice();
        }
    }
    
    class Simulation {       
        public:
            Simulation();  
            ~Simulation();
    
            void moveSlice(Tower &start_tower,
                           Tower &destination_tower);
            void recurFunction(int start,
                               int destination,
                               int slice_count,
                               Tower &tower_1,
                               Tower &tower_2,
                               Tower &tower_3);  
    
        private:
            int m_counter;     // Schrittzähler  
    };
    Simulation::Simulation() {
        m_counter = 0;
    }
    
    Simulation::~Simulation()
    {
    }
    
    //---------------------------------------------------------------------
    // Scheibe von Turm zu Turm bewegen
    //---------------------------------------------------------------------
    void Simulation::moveSlice(Tower &start_tower,
                               Tower &destination_tower) {            
    
        destination_tower.addSlice(start_tower.getSlice());
        start_tower.removeSlice();
    }
    
    //-------------------------------------------------------------------------
    // Rekursiver Methodenaufruf zur Berechnung & Anzeige der Möglichkeiten
    //-------------------------------------------------------------------------
    void Simulation::recurFunction(int start,           // Startturm
                                   int destination,     // Zielturm
                                   int slice_count,     // Anzahl Scheiben
                                   Tower &tower_1,
                                   Tower &tower_2,
                                   Tower &tower_3) {
    
        Display console;        // Konsole erstellen    
        stringstream step_no;   // Schrittnummer
        int free;               // freier Turm
    
        if (slice_count != 0) {        
            free = 6-start-destination;        
            // rekursiver Aufruf
            recurFunction(start,
                          free,
                          slice_count-1,
                          tower_1,
                          tower_2,
                          tower_3);        
    
            // Schritte anzeigen  
            console.gotoXY(0,0);
            step_no << ++m_counter << ".)  Turm "
                    << start << " nach -> Turm "
                    << destination << endl;    
    
            console.print(step_no);  
            console.gotoXY(1,26);
    
            // Ausgangszustand des Turms 1malig anzeigen
            if (m_counter == 1) {
                console.print(tower_1, 9, 15);
                console.gotoXY(1,26);
            }
    
            console.waitKeyboard();
    
            // Simulationsteil:
            if (start == 1 && destination == 2)
                moveSlice(tower_1, tower_2);        // Tower1->Tower2
            if (start == 1 && destination == 3)
                moveSlice(tower_1, tower_3);        // Tower1->Tower3
            if (start == 2 && destination == 1)
                moveSlice(tower_2, tower_1);        // Tower2->Tower1
            if (start == 2 && destination == 3)
                moveSlice(tower_2, tower_3);        // Tower2->Tower3
            if (start == 3 && destination == 1)
                moveSlice(tower_3, tower_1);        // Tower3->Tower1
            if (start == 3 && destination == 2)
                moveSlice(tower_3, tower_2);        // Tower3->Tower2          
    
            // neu zeichnen
            console.clr();
            console.print(tower_1, 9, 15);
            console.print(tower_2, 9, 42);
            console.print(tower_3, 9, 68);
    
            // erneuter rekursiver Aufruf
            recurFunction(free,
                          destination,
                          slice_count-1,
                          tower_1,
                          tower_2,
                          tower_3);        
        }      
        console.gotoXY(15,0);
    }
    
    //---------------------------------------------------------------------
    // Programm-Hauptfunktion
    //---------------------------------------------------------------------
    int main() {
    
        Slice slice[6]; // 6 Scheiben mit folgendem Aussehen
    
        slice[0].setSlice(          "***");
        slice[1].setSlice(        "*******");
        slice[2].setSlice(      "***********");
        slice[3].setSlice(    "***************");
        slice[4].setSlice(  "*******************");
        slice[5].setSlice("***********************");
    
        // 3 Türme erstellen
        Tower tower_1, tower_2, tower_3;
    
        // Turm_1 mit Scheiben auffüllen
        for (int i = 5; i > -1; --i) {
            tower_1.addSlice(slice[i]);
        }
    
        // Simulation starten
        Simulation sim;
        sim.recurFunction(1, 3, 6, tower_1, tower_2, tower_3);
        return 0;
    }
    

  • Mod


  • Mod

    Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    Was!?



  • SeppJ schrieb:

    http://en.wikipedia.org/wiki/Special_member_functions

    Meinst du die syntaktische Deklaration der Methoden ... dass sie so ungefährt deklariert werden sollten?



  • Arcoth schrieb:

    Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    Was!?

    Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM 🙂 Muss mir zu Weihnachten mal was Neues göhnen.


  • Mod

    Ach, du redest von der IDE... ich dachte, du redest von der eigentlichen ausführbaren Datei.



  • Cpp_Anfaeger schrieb:

    Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM 🙂 Muss mir zu Weihnachten mal was Neues göhnen.

    Ja, z.B. Linux.



  • göhner schrieb:

    Cpp_Anfaeger schrieb:

    Ich kann richtig mit verfolgen, wie die IDE bei der Autovervollständigung nachschlägt bei 1 GB RAM 🙂 Muss mir zu Weihnachten mal was Neues göhnen.

    Ja, z.B. Linux.

    Ja, wurde mir auch schon empfohlen. Ubuntu glaube ich war das ... soll ziemlich sehr klein sein.



  • Cpp_Anfaeger schrieb:

    Ufff ... Klassen sind schon umfangreicher und brauchen relativ mehr Speicher als normale Programmierung ...

    http://s14.directupload.net/images/131208/5afa74r2.jpg

    Soweit sind die Klassen fertig designed u. programmiert. Es scheint auch zu funktionieren.

    Klassen sind schon umfangreicher, aber man gewinnt Übersicht, wenn man es richtig anstellt. Mehr Speicher brauchen sie deshalb nicht.

    So umfangreich, wie Du es gemacht hast, brauchen sie auch gar nicht zu sein. Versuche nicht alles zwanghaft in eine Klasse zu packen. SeppJ hat Dir mit dem Link schon den Tipp gegeben, dass Du zumindest den Destruktor überall einsparen kannst. Der wird automatisch richtig erzeugt, solange Du einfache Member hast und nicht etwa Pointer mit allokiertem Memory.
    Die Klasse Simulation ist wahrscheinlich völlig überflüssig, da reicht eine Funktion. Bei der Klasse Display sind zumindest die print-Funktionen überflüssig; hier würde gleich eine Ausgabe auf std::cout reichen.
    Schau mal was Display sonst noch macht und was die Methode Simulation::recurFunction macht. In beiden stehen Größen und Abläufe, die über das Aussehen der Ausgabe entscheiden. Das ist ungünstig geschnitten, da sind zu viele implizite Schnittstellen zwischen beiden. D.h. wenn Du in der eine was änderst, musst Du in der anderen in der Regel auch Änderungen vornehmen.

    Die Zeilen 313 bis 328 lassen sich in einer kleinen for-Schleife zusammenfassen, wenn man den String für Slice gleich im Konstruktor mitgibt. Tipp: ein Objekt sollte 'gebrauchsfertig' sein, wenn der Konstruktor verlassen wird. Das spart dann auch gleich die Methode Slice::setSlice ein. So sähe es dann z.B. aus:

    // 3 Türme erstellen
        Tower tower_1, tower_2, tower_3;
        // Turm_1 mit Scheiben auffüllen
        string bild = "***********************";
        for (int i = 5; i > -1; --i) {
            tower_1.addSlice(Slice(bild));
            if( i > 0 )
                bild.erase( bild.end()-4, bild.end() );
        }
    

    Wenn man eine Klasse entwirft, sollte man sich zunächst darüber Gedanken machen, was die Klasse repräsentiert, welche Verantwortlichkeit sie hat und wie sie vom Anwender benutzt wird. Die Internas spielen zunächst keine Rolle.

    Beispiel Turm: Er repräsentiert den Stapel Scheiben, hält die Scheiben. Man muss eine Scheibe drauf legen und entfernen können - oder eine Scheibe von einem Turm zum anderen verschieben können. Es greift der Verschiebe-Algorithmus darauf zu und die Darstellung des Turms (Turm-View).
    Dein Turm:

    class Tower {        
        public:
            Tower();
            // ~Tower();  // muss nicht
    
            // -- Schnittstelle zum Verschiebe-Algorithmus
            void addSlice(Slice slice);   // 1x aufnehmen    
            Slice getSlice(); //oberste Scheibe zurückgeben
            void removeSlice(); // 1x löschen      
    
            // -- Schnittstelle zur Darstellung (View)
            Slice getAt(const int &iter); // irgendwo eine Scheibe
            int getSize(); // Towergröße auslesen
    
        // Member
    };
    

    das sieht doch schon mal sehr sauber aus. Nur weiter so - Übung macht den Meister 😉

    Gruß
    Werner



  • Danke dir für die guten Tipps zur Gestaltung der Klassen!

    Werner Salomon schrieb:

    SeppJ hat Dir mit dem Link schon den Tipp gegeben, dass Du zumindest den Destruktor überall einsparen kannst. Der wird automatisch richtig erzeugt, solange Du einfache Member hast und nicht etwa Pointer mit allokiertem Memory.

    Achso, deswegen hat der Designer von Qt Creator standardmäßig keine Destruktoren in die Vorlagen eingeblendet. Ich hatte schon gedacht, dass das ein Bug wäre 🙂

    Werner Salomon schrieb:

    In beiden stehen Größen und Abläufe, die über das Aussehen der Ausgabe entscheiden. Das ist ungünstig geschnitten, da sind zu viele implizite Schnittstellen zwischen beiden. D.h. wenn Du in der eine was änderst, musst Du in der anderen in der Regel auch Änderungen vornehmen.

    Ich habe versucht, die Klasse Display so gut wie möglich losgelöst zu erstellen. Inwieweit muss ich sie ändern, wenn ich in der Klasse Simulation etwas ändere? Ich habe gedacht, die Klasse Display gibt die Gebrauchsanweisung vor, wie sie zu benutzen ist, und daran muss jeder sich halten, der sie benutzen möchte.

    Werner Salomon schrieb:

    Die Zeilen 313 bis 328 lassen sich in einer kleinen for-Schleife zusammenfassen

    Ja, ich habe den Hang, alles so zu programmieren, damit ich sofort sehe, was vor sich geht. Dass da wiederum mehr Zeilen herauskommen ... super! Diese Technik ist wirklich neu für mich 👍 :

    // 3 Türme erstellen
        Tower tower_1, tower_2, tower_3;
        // Turm_1 mit Scheiben auffüllen
        string bild = "***********************";
        for (int i = 5; i > -1; --i) {
            tower_1.addSlice(Slice(bild));
            if( i > 0 )
                bild.erase( bild.end()-4, bild.end() );
        }
    


  • Cpp_Anfaeger schrieb:

    Werner Salomon schrieb:

    In beiden stehen Größen und Abläufe, die über das Aussehen der Ausgabe entscheiden. Das ist ungünstig geschnitten, da sind zu viele implizite Schnittstellen zwischen beiden. D.h. wenn Du in der eine was änderst, musst Du in der anderen in der Regel auch Änderungen vornehmen.

    Ich habe versucht, die Klasse Display so gut wie möglich losgelöst zu erstellen. Inwieweit muss ich sie ändern, wenn ich in der Klasse Simulation etwas ändere? Ich habe gedacht, die Klasse Display gibt die Gebrauchsanweisung vor, wie sie zu benutzen ist, und daran muss jeder sich halten, der sie benutzen möchte.

    ich meinte gar nicht Display sondern das main-Programm und die Simulation. In Der Methode Display::gotoXY() hast Du X und Y vertauscht - das verwirrt natürlich. X wird zur Zeilennummer (nach unten ansteigend) und Y ist die Spalte.

    Die Positionen in Simulation::recurFunction müssen sich nach der Größe und Anzahl der Scheiben im main richten. D.h. wenn Du bei dem einen wesentliche Änderungen durchführt, dann muss das andere auch angepasst werden.
    Außerdem ist Simulation::recurFunction direkt von Display abhängig, da es dort als lokale Variable benutzt wird. Das ist zumindest aus Sicht des Designs problematisch - natürlich weniger bei so einem kleinen Programm. Bei größeren Applikationen sollte man das besser trennen.

    Ich könnte mir einen move-Algorithmus vorstellen, der etwa so aussieht:

    template< typename View >
    void move( std::size_t n, Tower& src, Tower& via, Tower& dst, const View& view )
    {
        if( n == 1 )
        {
            move1( src, dst );
            view();
        }
        else
        {
            move( n-1, src, dst, via, view );
            move1( src, dst );
            view();
            move( n-1, via, src, dst, view );
        }
    }
    

    move1 ist eine (friend-)Funktion zum Verschieben eine Scheibe vom Turm src (Source) nach dst (Destination). view ist ein Funktor, in dem der Anwender die Ausgabe der Szene unterbringen kann.

    Cpp_Anfaeger schrieb:

    Werner Salomon schrieb:

    Die Zeilen 313 bis 328 lassen sich in einer kleinen for-Schleife zusammenfassen

    Ja, ich habe den Hang, alles so zu programmieren, damit ich sofort sehe, was vor sich geht.

    Das solltest Du auch so beibehalten. Was mich störte war eigentlich nur das S lice::setSlice . Folgendes wäre auch noch eine Alternative:

    Slice slices[] = {
            Slice(           "***" ),
            Slice(         "*******" ),
            Slice(       "***********" ),
            Slice(     "***************" ),
            Slice(   "*******************" ),
            Slice( "***********************" )
        };
    
        // 3 Türme erstellen + Turm_1 mit Scheiben auffüllen
        Tower tower_1( begin(slices), end(slices) ), tower_2, tower_3;
        // Tower tower_1( slices, slices + sizeof(slices)/sizeof(*slices) ), tower_2, tower_3; // bei C++98
    

    Dann brauch es noch einen weiteren Konstruktor:

    class Tower {        
        public:
            Tower();
            template< typename InItr >
            Tower( InItr first, InItr last )
                : m_tower_content( first, last )
            {}
    

    .. wobei ich auch auch auf die Slice-Klasse ganz verzichten würde. Es reicht, im Turm eine Menge von (Scheiben-)Ids abzulegen - von 1 für die kleinste Scheibe und dann fortlaufend aufsteigend. Und erst bei der Anzeige wird entschieden, wie eine Scheibe genau aussieht.

    Gruß
    Werner



  • Werner Salomon schrieb:

    In Der Methode Display::gotoXY() hast Du X und Y vertauscht

    Hmmm ... eigentlich dachte ich, dass Bill das in seiner Klasse vertauscht hatte 🙂 , weil ich das Ganze in meiner Klasse umdrehen musste, damit aus x = line wird bzw. y = column:

    void Display::gotoXY(const int &line, 
                         const int &column) {
    
        COORD p;
        p.Y = line;		// Zeile
        p.X = column;	// Spalte
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p);
    }
    

    Werner Salomon schrieb:

    Was mich störte war eigentlich nur das Slice::setSlice.

    Mich hat es auch gestört. Super ... wieder was gelernt ... ich hatte vorher nicht herausfinden können, wie man die Initialisierung von 6 Elementen auf einmal syntaktisch hinbekommt.

    Zu den Templates ... ich denke, dass ich noch viel mehr mit Template machen muss. Es wird ja oft gesagt, dass man das nur kurz machen sollte, um zu wissen, was das ist, da man es später kaum benutzt. Bisher dachte ich, dass man nur Template benutzt, wenn es darum geht, Schreibarbeit für Überladungen zu sparen. Sollte man generell Template verwenden? Hat es bezogen auf die Geschwindigkeit Vorteile, wenn man Template benutzt?


  • Mod

    Hat es bezogen auf die Geschwindigkeit Vorteile, wenn man Template benutzt?

    Nicht direkt. Templates sparen nur Schreib- und Wartungs-Aufwand. Sie machen an sich den Code nicht schneller.

    Sollte man generell Template verwenden?

    Nein, absolut nicht!



  • Arcoth schrieb:

    Hat es bezogen auf die Geschwindigkeit Vorteile, wenn man Template benutzt?

    Nicht direkt. Templates sparen nur Schreib- und Wartungs-Aufwand. Sie machen an sich den Code nicht schneller.

    Sollte man generell Template verwenden?

    Nein, absolut nicht!

    Habe ich mir auch gedacht ... bis ich diese Diskussion gelesen hatte. Ich weiß allerdings nicht, ob ich die Übersetzung richtig verstanden habe:

    http://stackoverflow.com/questions/8925177/c-templates-for-performance


  • Mod

    Cpp_Anfaeger schrieb:

    Habe ich mir auch gedacht ... bis ich diese Diskussion gelesen hatte. Ich weiß allerdings nicht, ob ich die Übersetzung richtig verstanden habe:

    http://stackoverflow.com/questions/8925177/c-templates-for-performance

    Das ist auch richtig. Templates sind in den Fällen viel schneller. Aber du musst gucken, gegenüber was sie schneller sind: Da geht es um den Weg, wie man in C generisch programmiert. Das funktioniert nämlich damit, dass man in C das Typsystem aushebelt, was typabhängige Optimierungen ziemlich unmöglich macht.

    template <typename T> void foo(T &t);
    
    void bar(int &i);
    
    void foobar(void *v);
    

    Mal angenommen, alle davon würden das gleiche tun. Dann wären foo und bar in allen Fällen gleich schnell, denn foo<int> wäre identisch zu bar. Der Vorteil von foo wäre, dass man sich nicht auf int beschränkt hätte. foobar wäre aber möglicherweise langsamer als die beiden anderen Varianten, selbst wenn man es mit einem entsprechend gecasteten int* aufruft. Denn wenn der Code für foobar erzeugt wird, dann kann der Compiler nicht wissen, womit die Funktion später mal aufgerufen wird und kann diesbezüglich weniger optimieren.



  • Achso, danke alles klar ... nur gegenüber C in diesen Fällen.



  • Das hat mit dem Typsystem oder dessen Umgehung nicht unbedingt was zu tun, sondern damit, dass die Generik in dem Fall erst zur Laufzeit aufgelöst wird. Etwas entsprechendes kannst du in C++ mit virtuellen Funktionen bauen, das hat die gleichen Nachteile.


Anmelden zum Antworten