Menü Klasse - Konstruktor Problem



  • @ Andrey

    Ja okay vll. sollte ich mich dann eher an den Vorschlag von "(D)Evil" halten, aber interessieren tut mich den Beispiel jetzt schon. Woher kommen die ganzen Funktion mit "va_list", "va_start"...?? Aus #include <stdio.h> oder aus #include <stdarg.h> ???
    Und was genau bewirken die?
    Kannst du mal erläutern was du genau in diesem Abschnitt machst?

    /*hier machst du mal ein paar Array, glaub ich und zwar soviele wie bei iMenuOptionCount angegeben wurden, und dann machst du da noch "new char" weil das alles zu Laufzeit noch nicht bekannt ist, und somit muss es auf den Heap. aber was ich nicht verstehe was soll das Sternchen "*" nach "new char", und warum ist "pMenuOption" eigentlich kein normaler Zeiger, sondern ein Zeiger auf einen Zeiger??? Also mit "**" */
            pMenuOption=new char* [iMenuOptionCount];
            //hier hab ich keinen plan was passiert
            va_list pArgList; 
            /* hier hab ich auch keinen Plan, hat aber doch irgendwas mit dem Konstruktor und den 3 "..." zu tun oder? */
            va_start (pArgList, iOptionCount);
            /* und hier übergibst du irgendwie das was man bei "..." im Konstruktor eingegeben hat an pMenuOption, bloß blick ich da net ganz durch wo das Zeug aus "..." jetzt genau herkommt */
            for(int i=0; i<iOptionCount; i++){
                pMenuOption[i]=va_arg(pArgList, char*);
            }
            /* und da wird irgendwie was beendet. Sowas wie delete? Also von der Stelle hab ich auch keinen Plan */
            va_end (pArgList);
    

    Bitte erkläre mir das doch ein bisschen, ich möchte den Code halt einfach verstehen.

    Dankeschön schon mal im Voraus.



  • an dieser stelle würde sich ein blick in die dokumentation lohnen... 🤡
    Aber na jut, was solls, wenn ich die geschicht schon angefangen hab^^

    also:
    Die sache mit "char* []" hab ich hier nur verwendet, um das ganze beispiel so "primitiv" wie möglich zu halten, ist eigentlich ganz leicht zu verstehen, wenn man sich vor augen führt, dass arrays "[]" sowas wie "bewegliche pointer" sind. Wenns dir mehr gefällt, kannst du "new char* [i]" durch "new string [i]" ersetzen. Ist auch besser lesbar...

    Dann zu "..."
    Wenn du eine funktion schreibst, die beliebig viele argumente akzeptiert, brauchst du <stdarg.h> sowie <stdio.h> (einfach beides #include'n).

    "va_list" ist ein datentyp für einen zeiger, der auf die argumente der funktion zeigt. Mit "va_list pArgList;" wird also erstmal so ein zeiger auf die funktionsparameter deklariert.

    "va_start" sorgt dafür, dass dieser zeiger richtig initialisiert wird, und tatsächlich auf die argumente, die anstelle von "..." kommen, zeigt. Dieses makro benötigt also den zeiger selbst, sowie den bezeichner des letzten gewöhnlichen parameters, der vor "..." kommt: in diesem fall war es "iOptionCount"

    mit "va_arg" kannst du dann die parameter aus deiner parameterliste auslesen. Deswegen muss va_arg natürlich wissen, wo (zeiger) es lesen soll, und auserdem was (datentyp) es aus der parameterliste lesen soll (weil bei "..." die datentypen nicht explizit angegeben werden [daher kommt die unsicherheit ⚠ ] )
    Nachdem va_arg den parameter eingelesen hat, versetzt es ausserdem den pArgList pointer (zB bei "char" um einen byte, bei "int" um vier bytes etc) damit er immer auf den nächsten parameter zeigt.

    wozu va_end genau benötigt wird, weis ich auch nicht, sollte man aber jedes mal hinzufügen, ansonsten kann vorhersehbares verhalten nicht garantiert werden.

    so, hoffentlich ist jetzt dieser ansatz klarer geworden. Aber wie gesagt: sicher ist es nicht... ⚠



  • diese "..."-funktionen sollte man eigentlich, wenn es geht, nur dort verwenden, wo der fehler a)keine katastrophale folgen hat b)relativ leicht zu finden ist. Wenn der code 100% bombensicher sein soll, sollte man's lieber sein lassen... 😃



  • Vor allem erzeugt die Ellipse mit C++-Objekten undefiniertes Verhalten. Das schränkt die Benutzbarkeit noch weiter ein.



  • inwiefern erzeugt es denn ein undefiniertes verhalten?



  • Was heißt "inwiefern"? Tu ein C++-Objekt in eine Ellipsenfunktion und es kann Beliebiges passieren. Klar, da kann auch das Richtige dabei sein, aber das ist eben nicht garantiert.



  • Stelle ich etwas vollkommen falsches unter "c++ Objekt" vor?

    #include <stdio.h>
    #include <iostream>
    #include <stdarg.h>
    
    using namespace std;
    
    class Vector2D{        //das hier ist doch eine klasse?
    public:
    	float x; float y;
    	Vector2D(){
    		x=0.0f; y=0.0f;
    	}
    	Vector2D(float _x, float _y){
    		x=_x; y=_y;
    	}
    };
    
    Vector2D average(int iNumVectors, ... ){        //das ist doch eine ellipse?
    	Vector2D v=Vector2D(0.0f, 0.0f);
    	Vector2D arg;
    
    	va_list pArgs;
    	va_start(pArgs, iNumVectors);
    	for(int i=0; i<iNumVectors; i++){
    		arg=va_arg(pArgs, Vector2D);
    		v=Vector2D(v.x+arg.x, v.y+arg.y);
    	}
    
    	v=Vector2D(v.x/iNumVectors, v.y/iNumVectors);
    	return v;
    }
    
    int main(){
        //und instanzen von klassen sind doch "Objekte" oder?
        Vector2D middlepoint=average(3,
    				Vector2D(0.0f,0.0f),
    				Vector2D(0.0f,2.0f),
    				Vector2D(3.0f,1.0f));
        cout<<"x="<<middlepoint.x<<" y="<<middlepoint.y<<endl;
        return 0;
    }
    

    Also, ich weis jetzt nicht, was du mit dem "undefinierten verhalten" meinst...
    Klappt doch alles wunderbar 😕



  • So ich hab jetzt hier auch mal selber noch mal eine Klasse Menü geschrieben. Wie findet ihr die? Ist aber doch sicherlich besser als meine erste oder? 🙂

    standard.h

    #ifndef STANDARD_H_INCLUDED
    #define STANDARD_H_INCLUDED
    
    #include <string>
    
    class Array
    {
        public:
        Array();
        virtual ~Array();
        void change_size(std::string **TempArray,int laengeALT,int laengeNEU);
    };
    
    class menu : public Array
    {
        public:
        menu();
        ~menu();
        void add_menu_point(std::string name);
        void Draw(int interval=0);
        private:
        std::string *P;
        int number_of_P;
    };
    
    #endif // STANDARD_H_INCLUDED
    

    standard.cpp

    #include "standard.h"
    #include <iostream>
    
    //###class Array ###
    Array::Array()
    {
    
    }
    
    Array::~Array()
    {
    
    }
    
    void Array::change_size(std::string **TempArray,int laengeALT,int laengeNEU)
    {
        std::string *temp=new std::string[laengeALT];
        for (int i=0;i<laengeALT;i++)
        {
            temp[i]=(*TempArray)[i];
        }
        delete [](*TempArray);
        *TempArray=0;
        *TempArray=new std::string[laengeALT+laengeNEU];
        for (int i=0;i<laengeALT;i++)
        {
            (*TempArray)[i]=temp[i];
        }
        delete []temp;
    }
    
    //### class menu ###
    menu::menu()
    :P(new std::string[1]),number_of_P(1)
    {
    
    }
    
    menu::~menu()
    {
        delete P;
    }
    
    void menu::Draw(int interval)
    {
        for (int i=0;i<number_of_P;i++)
        {
            std::cout << P[i] << "\n";
            for (int j=0;j<interval;j++)
            {
                std::cout << "\n";
            }
        }
    }
    
    void menu::add_menu_point(std::string name)
    {
        change_size(&P,number_of_P,1);
        P[number_of_P-1]=name;
        number_of_P++;
    }
    

    main.cpp

    #include "standard.h"
    
    int main()
    {
        menu Menu;
        Menu.add_menu_point("NEU STARTEN");
        Menu.add_menu_point("WEITER");
        Menu.add_menu_point("ZURUECK");
        Menu.add_menu_point("OPTIONEN");
        Menu.add_menu_point("ENDE");
        Menu.Draw();
    
        return 0;
    }
    

    So, das is jetzt halt nur mit Array's. Mit "std::vector<...>" wäre es sicherlich professioneller, aber ich hatte mir vorgenommen nur das zu verwenden was ich bis jetzt in meinem C++ Buch gemacht habe. Ich weiß zwar schon ungefähr wie man mit vector arbeitet (aus nem früheren Tutorial), aber wollte ihn erster Linie eigentlich nur mit dem Wissen aus meinem Buch arbeiten.
    Wie kann ich das jetzt noch verbessern? Dankeschön schon mal im Voraus.



  • Naja das ganze Pointerrumschuppsen muss nicht sein. Kommentier das wenn dann ein wenig ... ist nämlich sicherlich nicht gerade ein schöner Ansatz ... selbst für Pointerschubbsen 😉

    Warum nehmt ihr nicht vectoren? Das ist sicher und ihr müsst euch um fast nichts kümmern.



  • So ich habs jetzt nochmal bisschen verbessert und mit der "Improved Console" gearbeitet!! Seit heute liebe ich die Improved Konsole!! Gibts was besseres? Also das ding is echt "göttlich"!!!! Man kann jetzt sogar mit "W" und "S" wie in nem "echten Menü" hoch und runter switchen!!!
    Hier mal meinen Code, vll. schaut es ja jemand an:

    main.cpp

    #include "menu.h"
    
    int main()
    {
        menu Menu;
        Menu.add_menu_point("NEU STARTEN");
        Menu.add_menu_point("WEITER");
        Menu.add_menu_point("ZURUECK");
        Menu.add_menu_point("OPTIONEN");
        Menu.add_menu_point("ENDE");
        while (1)
        {
            Menu.Draw();
        }
    
        return 0;
    }
    

    menu.cpp

    #include "menu.h"
    #include <conio.h>
    #include <iostream>
    #include "ic.h"
    
    using namespace ic;
    using namespace shorties;
    
    //###class Array ###
    Array::Array()
    {
    
    }
    
    Array::~Array()
    {
    
    }
    
    //Funktion um ein Array nachträglich zu vergrößern
    void Array::change_size(std::string **TempArray,int laengeALT,int laengeNEU)
    {
        std::string *temp=new std::string[laengeALT];
        for (int i=0;i<laengeALT;i++)
        {
            temp[i]=(*TempArray)[i];
        }
        delete [](*TempArray);
        *TempArray=0;
        *TempArray=new std::string[laengeALT+laengeNEU];
        for (int i=0;i<laengeALT;i++)
        {
            (*TempArray)[i]=temp[i];
        }
        delete []temp;
    }
    
    //### class menu ###
    menu::menu()
    :P(new std::string[1]),number_of_P(1),
    key(0),chapter(0)
    {
    
    }
    
    menu::~menu()
    {
        delete P;
    }
    
    //schreibt die menü namen hin und regelt das menü
    void menu::Draw(int interval)
    {
        for (int i=0;i<number_of_P;i++)
        {
            animation(i);
    
            std::cout << P[i] << "\n";
            for (int j=0;j<interval;j++)
            {
                std::cout << "\n";
            }
        }
        keystroke();
        key_controll();
        con.setCurPos(0,0);
        con.clearColor(FG_RED);
    }
    
    //funktion um einen neuen menüpunkt hinzuzufügen
    void menu::add_menu_point(std::string name)
    {
        change_size(&P,number_of_P,1);
        P[number_of_P-1]=name;
        number_of_P++;
    }
    
    //speichert die atseneingabe
    short menu::keystroke()
    {
        key=getch();
        return key;
    }
    
    //erstellt die textfarbe anhand der improved console
    void menu::animation(int counter)
    {
        con.setTextColor(FG_WHITE);
        if (chapter==counter) con.setTextColor(FG_RED);
    }
    
    //kontrolliert das "switchen" mit der "S" und "W" Taste, und regelt es
    void menu::key_controll()
    {
        if (key==115)
        {
            chapter=chapter+1;
        }
        if (key==119)
        {
            chapter=chapter-1;
        }
        if (chapter>number_of_P-2)
        {
            chapter=number_of_P-2;
        }
        if (chapter<0)
        {
            chapter=0;
        }
    }
    

    menu.h

    #ifndef STANDARD_H_INCLUDED
    #define STANDARD_H_INCLUDED
    
    #include <string>
    
    class Array
    {
        public:
        Array();
        virtual ~Array();
        void change_size(std::string **TempArray,int laengeALT,int laengeNEU);
    };
    
    class menu : public Array
    {
        public:
        menu();
        ~menu();
        void add_menu_point(std::string name);
        void Draw(int interval=0);
        short keystroke();
        void animation(int counter);
        void key_controll();
        private:
        std::string *P;
        int number_of_P;
        short key;
        short chapter;
    };
    
    #endif // STANDARD_H_INCLUDED
    

    Und "include "ic.h" " damit ist natürlich die cooooole Improved Konsole gemeint. Ich finds echt toll, es ist zum ersten mal das eines meiner Programme ein bisschen Frabe abekommen hat.



  • Andrey schrieb:

    Also, ich weis jetzt nicht, was du mit dem "undefinierten verhalten" meinst...
    Klappt doch alles wunderbar 😕

    Um nochmal hier drauf einzugehen: Das klappt? Prima! Ist trotzdem undefiniert...

    Und genau das ist das Problem an undefiniertem Verhalten: Es ist nicht gesagt dass es nicht klappt.



  • Stromberg schrieb:

    ...

    menu(string P1);                                                             // }
        menu(string P1,string P2);                                                   // }
        menu(string P1,string P2,string P3);                                         // } Konstruktor überladen, sodass man
        menu(string P1,string P2,string P3,string P4);                               // } die Anzahl der Menüpunkt aussuchen
        menu(string P1,string P2,string P3,string P4,string P5);                     // } kann. (Maximaleanzahl=7 Menüpunkte)
        menu(string P1,string P2,string P3,string P4,string P5,string P6);           // }
        menu(string P1,string P2,string P3,string P4,string P5,string P6,string P7); // }
    

    ...

    Äh ... warum nimmst Du nicht Defaultparameter (falls es nur relativ wenige Parameter sind) oder einen

    menu(vector<string> const& allePs);
       // Geht natürlich nicht, wenn man unterschiedliche Typen hat oder die Initialisierungsliste nutzen muss
    

    Halte ich für wesentlich einfacher, übersichtlicher und sicherer ...

    Gruß,

    Simon2.



  • LordJaxom schrieb:

    Um nochmal hier drauf einzugehen: Das klappt? Prima! Ist trotzdem undefiniert...

    Und genau das ist das Problem an undefiniertem Verhalten: Es ist nicht gesagt dass es nicht klappt.

    also, das will ich jetz aber wirklich verstehen! :p
    So, hab hier dieselbe funktion nochmal ein bisschen anders umgeschrieben, ohne dieser ganzen makros (in wirklichkeit sehen die makros auch nicht viel anders aus, nur n bisschen schwerer lesbar, finde ich... ) Wie auch immer, die selbstgebackenen funktionieren auch.

    Vector2D myAverageEllipseFunction(int iNumArgs, ...){
    
    	//entspricht in etwa: va_list pArgList
    	char* pArgList;
    
    	//entspricht in etwa: va_start(pArgList,iNumArgs)
    	pArgList=(char*)((long)&iNumArgs+sizeof(iNumArgs));
    
    	Vector2D v=Vector2D(0.0f, 0.0f);
    	Vector2D arg;
    
    	for(int i=0; i<iNumArgs; i++){
    
    	//argument lesen, entspricht in etwa va_arg(pArgList,Vector2D);
    		arg=*((Vector2D*)(pArgList));
    		//pointer versetzen, macht auch va_arg
    		pArgList=(char*)((long)pArgList+sizeof(Vector2D));
    
    		v=Vector2D(v.x+arg.x, v.y+arg.y);
    
    	}
    
    	//was va_end genau amcht, weis ich nicht
    	pArgList=(char*)0;
    
    	v=Vector2D(v.x/iNumArgs, v.y/iNumArgs);
    	return v;
    }
    

    Sieht zwar ziemlich finster aus, dafür sind hier keine makros, von den unklar ist, was die eigentlich tun. 🤡
    Für diese ellipse braucht man nur sizeof() und casting in (char*) und zurück.
    Frage: was davon funktioniert bei c++ Objekten anders als bei primitiven datentypen wie char oder int? solange alle argumente nacheinander im stack abgelegt werden, ist doch alles in ordnung. Oder? 🙄



  • Dann verstehe: Objekte über Ellipsen zu übergeben ist undefiniert. Das sagt der Standard. Der Standard gibt die Sprache vor. Du programmierst in der Sprache.

    Was sagt uns das? Es ist vollkommen uninteressant, ob das auf Deiner Plattform / mit Deinem Compiler / .... jetzt gerade funktioniert, denn es ist nicht garantiert, dass es woanders auch funktioniert. Es ist nichtmal garantiert dass es unter denselben Voraussetzungen in der nächsten Mondphase noch funktioniert. Es ist schlicht und ergreifend undefiniert.

    Über das Warum mag ich hier garnicht philosophieren, auch weil ich die Gründe selbst nicht vollständig kenne. Aber da es laut Standard undefiniert ist brauche ich garnicht viel drüber nachdenken, warum (und unter welchen Umständen) es dennoch funktionieren könnte :p

    Ich habe gesprochen (How!) 😃



  • @Simon2
    Jop das hab ich auch schon erkannt des es so ziemlich unübersichtilich ist, deshlab hab ich doch nochmal eine neue "menu Klasse" geschreiben, in der man nun die Menüpunkte über die Funktion add_menu_point("blablab") hinzufügen kann. Und wie schon gesagt ich wollte nicht mit vector arbeiten.



  • Ich bitte die Adminstratoren bzw Moderatoren diesen Thread zu schließen. Mehrere saubere Lösungsvorschläge wurden bereits presentiert, doch scheint der Threadersteller nicht einzusehen, das seine Lösung einem äußerst unsauberen Code-Stil entspricht.

    @Threadersteller:
    Sorry wenn ich die Mods jetzt bitte den Thread zu schließen ... aber guck dir erstmal dein Buch weiter an bevor du die Klasse schreibst ... es bringt dir nix hier wildes Pointerschubbsen zu betreiben ... wenn du bei vectoren usw. bist kannst du dich nochmal an die Aufgabe wagen und dir dann evt. nochmal meinen oder auch die Lösungsvorschläge anderer anzugucken.



  • bevor das ganze hier geschlossen wird:
    @LordJaxom:

    LordJaxom schrieb:

    Es ist vollkommen uninteressant, ob das auf Deiner Plattform / mit Deinem Compiler / .... jetzt gerade funktioniert:D

    okay, ich muss zugeben, dass ich das auf einer einzigen plattform, mit einem einzigen compiler getestet habe 👎 kann mir jedoch trotzdem nicht vorstellen, dass es anderswo nicht funktioniert, weil es mir als die einzige logische lösung erscheint, einfach alle argumente nacheinander im speicher abzulegen. Na gut, der compiler wirds schon besser wissen. Dann ists halt undefiniert.

    @(D)Evil: naja, der threadersteller hat das ganze hier ja nicht vollgemüllt: ich wars 😃 Ausserdem kann man doch nicht von jedem gleich verlangen, dass er von anfang an immer nur mit den perfekten lösungen weiterarbeitet, wenn der Stromberg mit den vectoren klarkommt, wird er bestimmt einsehen, dass du von anfang an recht hattest 🤡



  • @(D)Evil
    Also ich weiß nicht was jetzt dein Problem genau ist? Ich will doch bloß nicht mit "vector::std" arbeiten. Also ich persönlich wüsste jetzt keinen Grund warum man den thread hier schließen sollte. Außerdem ist der Thread nun sowieso für mich abgeschlossen gewesen, weil ja eigentlich alle gesagt wurde. Aber ich will da niemand im wege stehen wenn sich (D)Evil freut und erst wieder ruhig schlafen kann wenn der Thread hier geschlossen ist, von mir aus dann schließt ihn, kein Problem. 🙂
    Und was du genau mit deinem Pointerschubsen meinst, verstehe ich auch nicht. Vll. ist es mit "std::vector" einfacher, aber ich weiß nicht was gegen meine Klasse spricht? (siehe Beitrag vom 00:05:25 18.03.2007 "Seite2").
    Okay vll. ist die nicht so professionell, also ich bin zufrieden mit der.
    Und was ist z.B. an der Funktion "change_size" falsch? Es ist doch eine gute Übung mal so eine Funktion zu schreiben, um zu schauen wie es geht ein Array nachträglich zu vergrößern.
    Und der Rest finde ich ist selbsterklärend und ich finde das gar nicht unnötig kompliziert. Ich denk schon das man die Klasse um einiges, in deinen oder in manchen anderen Augen hier besser schreiben kann, aber ich bin damit zufrieden.



  • Stromberg schrieb:

    ...Und wie schon gesagt ich wollte nicht mit vector arbeiten.

    Sorry, habe ich wohl übersehen.
    Warum denn nicht ?
    Mit Deinem "add_menu_point()-Ansatz" veränderst Du natürlich grundlegend Verhalten und Design Deiner Klasse: Sie hat nun einen "fließenden Zustand" und nicht einen, der zur "Geburt" festgelegt wird.
    Vorteil: Du kannst konsistenter zur Laufzeit ändern.
    Nachteil: Du musst sehr aufpassen, dass Du immer in einem konsistenten Zustand bist (sprich: "Immer alles ermöglichen und überprüfen").

    Kann sein (und Dein Projekt klingt auch danach), dass es genau das ist, was Du willst/brauchst.
    Wenn es aber anders ist, und Du schon eine "feste Klasse" (mein persönlicher Ausdruck; damit meine ich: Man kann nur ein gültiges Objekt anlegen und spart sich damit Initialisierungs- uind Prüforgien) brauchst, solltest Du dem vector nochmal eine Chance geben.
    Das hat nichts mit "ich brauch's eben nicht so professionell" zu tun, sondern einfach damit, dass vector Dir genau die Arbeit UND die Möglichkeiten, Fehler zu machen, abnimmt. Gerade Anfängern würde ich eher zum vector raten als zum "Rad selbst erfinden" (Gerade, weil dieses "Rad" relativ lang den Anschein wahren kann, zu funktioneren, um Dir dann umso herzhafter und unbarmherziger in den Hinter zu beißen). ... und dieses "Rad" haben Andere (Bessere) schon lange besser erfunden ... und "vector" genannt. 😉

    Kurz gesagt: Mit vector ist es einfacher, kürzer, sicherer, übersichtlicher und wartbarer => und das alles ohne Nachteil.

    Gruß,

    Simon2.



  • der nachteil ist, dass man dann an einem düsenjet herumbastelt, ohne zu wissen, wie ein rad funktioniert 😉
    Ich finde, zu übungszwecken ist es eigentlich ganz gut, sich n paar mal von solchen selbstgebastelten rädern "in den Hintern beissen zu lassen", statt gleich eine optimale lösung irgendwo rauszukopieren, das härtet ab 😃 👍


Anmelden zum Antworten