Umsetzungsproblem: Verkettete Liste als Klasse



  • nein. Schau dir mal im C++-Buch deiner Wahl die Bedeutung von static an. Das sorgt dafür, dass es die entsprechende Variable nur genau einmal pro Klasse gibt.



  • Schau dir mal im C++-Buch deiner Wahl die Bedeutung von static an

    Und ein kurzer Blick in unsere FAQ kann auch nicht schaden:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-39472.html
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-39471.html



  • Ich hab schon in ein paar Büchern/Tutorials gelesen, static aber bisher nur so aufgefaßt, daß der Wert der Variable zwischen den Aufrufen erhalten bleibt 😉

    Ich habe es jetzt mal wie vorgeschlagen eingefügt, meine CZeit.h sieht jetzt so aus:

    CZeit.h

    class CZeit
    {
        long *diff;
        static long start, ende;
        public:
            CZeit(void); //Konstruktor
            void SetzeStart(void); //
            void SetzeEnde(void); //
            float BerechneDiff(void); //
            ~CZeit(void); //Destruktor
            CZeit *next; //Zeiger auf nächste Element
    };
    

    Allerdings habe ich nun das Problem, dass sich die Static-Variablen nicht mit

    static long CZeit::start = 0;
    

    initialisieren lassen, der Compiler liefert mir da

    `static' may not be used when defining (as opposed to declaring) a static data member



  • UNeverNo schrieb:

    Allerdings habe ich nun das Problem, dass sich die Static-Variablen nicht mit

    static long CZeit::start = 0;
    

    initialisieren lassen, der Compiler liefert mir da

    `static' may not be used when defining (as opposed to declaring) a static data member

    Bei der Definition bzw. der Initialisierung einer Statischen Element-Variablen musst du die static Deklaration weglassen.
    D.h.

    long CZeit::start = 0;
    

    Caipi



  • Danke, das hat mir schon einmal weitergeholfen 🙂

    Jetzt habe ich das nächste Problem und zwar in der

    CZeit.cpp

    void CZeit::SetzeEnde(void)        
    {
        CZeit *element = new CZeit;
        element->diff = 15;
        element->next = anker;
        anker = element;
    }
    

    Ich muß das anker-Element irgendwo definieren

    CZeit *anker = 0;
    

    so daß es in allen Methoden verfügbar ist. Wie geht das 😕

    edit: Hat sich erledigt 🙂



  • Das Programm läuft soweit, allerdings funktioniert es nicht richtig. Muß ich mit Referenzen arbeiten (wenn ja, wo und wie, wenn nein woran liegt es dann)?

    CZeit.h

    class CZeit
    {
        long diff; //speichert start-ende ab
        static long start, ende; //zwischenspeicher
        public:
            CZeit(void); //Konstruktor
            void SetzeStart(void); //speichert Startzeit auf start
            void SetzeEnde(void);   //speichert Endzeit auf ende, und Differenz auf element->diff
            float BerechneDiff(void); //summiert alle diff auf, dividiert diese durch anzahl diff, und dient gleichzeitig als destruktor
            CZeit *next; //Zeiger auf nächste Element
    };
    

    CZeit.cpp

    #include <time.h>
    #include <iostream>
    #include <Chris/CZeit/CZeit.h>
    using namespace std;
    
    long CZeit::start = 0;
    long CZeit::ende = 0;
    CZeit *anker = 0;
    
    CZeit::CZeit(void)
    {
        start = 0;
        ende = 0;
    }
    
    void CZeit::SetzeStart(void)
    {
        start = clock();
    }
    
    void CZeit::SetzeEnde(void)        
    {
        ende = clock();
        CZeit *element = new CZeit;
        element->diff = (ende - start);
        element->next = anker;
        anker = element;
    }
    
    float CZeit::BerechneDiff(void)
    {
        long temp = 0;
        int i = 0;
        CZeit *old;
        while (anker)
        {
            temp = temp + anker->diff;
            old = anker;
            anker = anker->next;
            delete old;
        }    
        return temp / i / CLOCKS_PER_SEC;
    }
    

    main.cpp

    #include <time.h>
    #include <iostream>
    #include <Chris/CZeit/CZeit.h>
    
    using namespace std;
    
    int main(void)
    {
        CZeit zeitmessung;
        for (int i = 0; i < 10; i++)
        {
            zeitmessung.SetzeStart();
            //Beginn zu testender Algorithmus
            for (int j = 0; j < 5000; j++)
            {
                long k;
                k = j * j * j;
                cout << j << ": " << k << endl;
            }    
            //Ende
            zeitmessung.SetzeEnde();
        }       
        cout << "Durchschnittlich verbrauchte Zeit: " << zeitmessung.BerechneDiff();
        getchar();
    
        return 0;
    }
    

    Danke, ein (letztes?) Mal 😃



  • Hmm - mir ist gar nicht klar wieso Du hier eigentlich die Liste benötigst. Was hälst Du von dieser - ganz anderen Lösung?

    #include <iostream>
    #include <time.h>
    
    class CUhr
    {
    public:
        CUhr()
            : m_start( 0 )
            , m_sum( 0 )
            , m_nStopps( 0 )
        {}
    
        struct Stopper;
        friend Stopper;
        struct Stopper // noncopyable
        {
            explicit Stopper( CUhr& uhr )
                : m_uhr( uhr )
            {
                m_uhr.Start();
            }
            ~Stopper()
            {
                m_uhr.Stopp();
            }
        private:
            CUhr& m_uhr;
        };
    
        friend std::ostream& operator<<( std::ostream& out, const CUhr& uhr )
        {
            return out << double( uhr.m_sum ) / CLOCKS_PER_SEC / uhr.m_nStopps << " Sek";
        }
    
    private:
        void Start() 
        { 
            m_start = clock(); 
        }
        void Stopp()
        {
            m_sum += clock() - m_start;
            ++m_nStopps;
        }
    
        clock_t m_start;
        clock_t m_sum;
        int     m_nStopps;
    };
    
    int main()
    {
        using namespace std;
        CUhr uhr;
        for (int i = 0; i < 10; i++)
        {
            CUhr::Stopper stopper( uhr );
            //Beginn zu testender Algorithmus
            for (int j = 0; j < 5000; j++)
            {
                long k;
                k = j * j * j;
                cout << j << ": " << k << endl;
            }    
            //Ende
        }   // Scope-Ende stoppt die Uhr in ~Stopper      
        cout << "Durchschnittlich verbrauchte Zeit: " << uhr << endl;
        getchar();
    
        return 0;
    }
    

    Gruß
    Werner



  • In dem Konstruktor von CZeit

    UNeverNo schrieb:

    CZeit::CZeit(void)
    {
        start = 0;
        ende = 0;
    }
    

    setzt Du die statischen Variablen 'start' und 'ende' auf 0. Wenn man einen Konstruktor aufruft, so wie hier:

    UNeverNo schrieb:

    void CZeit::SetzeEnde(void)        
    {
        ende = clock();
        CZeit *element = new CZeit;
        element->diff = (ende - start);
        element->next = anker;
        anker = element;
    }
    

    wird 'ende' erst mit der aktuellen Zeit belegt und danach inklusive 'start' wieder auf 0 gesetzt. 🕶

    An dieser Stelle

    UNeverNo schrieb:

    float CZeit::BerechneDiff(void)
    {
        long temp = 0;
        int i = 0;
        CZeit *old;
        while (anker)
        {
            temp = temp + anker->diff;
            old = anker;
            anker = anker->next;
            delete old;
        }    
        return temp / i / CLOCKS_PER_SEC;
    }
    

    hast Du vergessen 'i' in der Schleife zu inkrementieren. Du teilst immer durch 0.

    Noch ein Tipp: Initialisieren die Member in der Initalisierungsliste im Konstruktor. Dann wird's einfacher - etwa so:

    CZeit::CZeit( long Diff, CZeit* Next )
        : diff( Diff )
        , next( Next )
    {}
    
    void CZeit::SetzeEnde()
    {
        anker = new CZeit( clock() - start, anker );
    }
    

    Gruß
    Werner



  • Mhh bisher hatte ich es so verstanden, dass ein Konstruktor aufgerufen wird, wenn ein Objekt erzeugt wird, d.h. in meinem Fall

    CZeit *anker = 0;
    

    , nicht wenn ich eine Methode aufrufe 😕

    Ich habe den Konstruktor jetzt erst einmal ganz herausgenommen, das mit dem i++; hinzugefügt 🙄 und noch ein Cast hinzugefügt. Dadurch funzt dans Programm nun 🙂

    Allerdings verstehe ich folgende Zeilen von Dir nicht.

    CZeit::CZeit( long Diff, CZeit* Next )
        : diff( Diff )
        , next( Next )
    {}
    
    void CZeit::SetzeEnde()
    {
        anker = new CZeit( clock() - start, anker );
    }
    

    Kannst Du mir das bitte noch einmal etwas näher erläutern? Danke.



  • UNeverNo schrieb:

    Mhh bisher hatte ich es so verstanden, dass ein Konstruktor aufgerufen wird, wenn ein Objekt erzeugt wird, d.h. in meinem Fall

    CZeit *anker = 0;
    

    , nicht wenn ich eine Methode aufrufe 😕

    Das ist kein Konstruktor, sondern eine (statische) Variable. Der Konstruktor wird immer dann aufgerufen, wenn Du ein Objekt der Klasse anlegst. Hier auf dem Heap

    CZeit *element = new CZeit;
    

    und hier auf dem Stack

    CZeit zeitmessung;
    

    UNeverNo schrieb:

    Allerdings verstehe ich folgende Zeilen von Dir nicht.

    CZeit::CZeit( long Diff, CZeit* Next )
        : diff( Diff )
        , next( Next )
    {}
    
    void CZeit::SetzeEnde()
    {
        anker = new CZeit( clock() - start, anker );
    }
    

    Kannst Du mir das bitte noch einmal etwas näher erläutern? Danke.

    Also die erste Methode ist ein Konstruktor mit zwei Parametern. Ein Objekt der Klasse CZeit hat zwei Member 'diff' und 'next'. Grundsätzlich sollte man es anstreben, die Member einer Klasse in der Initialisierungsliste zu belegen. Die Initialisierungsliste ist die Konstruktion die mit ':' beginnt.

    Ich hol' mal bißchen weiter aus. Stell Dir folgende Klasse vor.

    class Klasse1
    {
    public:
        Klasse1();  // Konstruktor
        std::string m_txt; // Member
    };
    

    Angenommen im Konstruktor von Klasse1 soll der Member 'm_txt' belegt werden. Also

    Klasse1::Klasse1()
    {
        m_txt = "irgendwas";
    }
    

    Zu dem Zeitpunkt zu dem 'm_txt = ' aufgerufen wird, muss m_txt bereits ein vollständig initialisiertes Objekt sein, sonst geht die Zuweisung in die Hose. Also muss vorher implizit ein (Default-)Konstruktor von std::string aufgerufen worden sein, da std::string kein trivialer Typ ist - es wird hier dynamisch Speicher verwaltet.
    Angenommen die Designer von std::string hätten entschieden, dass sie quasi auf Verdacht 8 Byte Speicher allokieren. Dann würde man bei der Zuweisung von "irgendwas" diesen wieder verwerfen und mindestens 10 Byte allokieren, damit 'irgendwas\0' reinpasst. Das wäre dann ziemlich imperformant erst 8 Byte allokieren, dann wieder löschen für nichts.
    Schlauer wäre es doch, dem Member 'm_txt' schon vor (!) seiner Geburt den String mitgeben zu können, damit er von vornherein weiß, wieviel Speicher er anlegen muss. Also besser:

    Klasse1::Klasse1()
        : m_txt( "irgendwas" )
    {}
    

    Hier wird jetzt nicht implizit der Default-Konstruktor von std::string, sondern explizit der Konstruktor aufgerufen, der ein Literal als Parameter aufnimmt. Dieser Konstruktor kann jetzt die Länge des Strings feststellen, allokiert passend Spiecher, rein mit dem String und alles ist gut. Kein Weg war vergebens 🙂 .

    Das und auch die Tatsache, dass alle Member 'vernünftige' Werte annehmen sollte, bevor (!) das '{' des Konstruktors erreicht wird, sind der Grund für die Initialisierungsliste.

    Der Ausdruck

    anker = new CZeit( clock() - start, anker );
    

    macht jetzt genau das selbe wie

    ende = clock();
        CZeit *element = new CZeit;
        element->diff = (ende - start);
        element->next = anker;
        anker = element;
    

    Die Variable 'ende' wird wie eine lokale Variable benutzt, einmal mit clock() beschrieben und anschließend nur einmal benutzt und danach nie wieder, also kann man sie weglassen.

    CZeit *element = new CZeit;
        element->diff = (clock()- start);
        element->next = anker;
        anker = element;
    

    Die Initialisierung der Member 'diff' und 'next' habe ich in den (neuen) Konstruktor verschoben (s.o.). Daher brauche (darf) ich es später nicht mehr tun.

    CZeit *element = new CZeit( clock()- start, anker );
        anker = element;
    

    Ja - und jetzt sieht man das die lokale Variable 'element' auch nur einmal beschrieben und benutzt wird, also kann man sie auch weglassen.

    anker = new CZeit( clock()- start, anker );
    

    Gruß
    Werner



  • Ah - ich sehe gerade, dass nicht nur die Leute die Fragen haben, vorher die FAQ lesen sollten, sondern auch jemand der antwortet.
    siehe auch hier http://www.c-plusplus.net/forum/viewtopic-var-t-is-39484.html 😉



  • Ich sehe, daß Du Dich schon etwas länger mit C++ befaßt 😉

    Ich beginne so langsam Deinen Code zu verstehen, danke für die Erläuterungen.

    Das mit ende hatte ich gemacht, um genauere Werte zu bekommen, denn ich dachte mir wenn ich z.B. [cpp]diff = clock() - start;[/code] code, diese Aktion auch schon wieder Zeit verbraucht, kann mich natürlich auch irren 🙂


Anmelden zum Antworten