Menü Klasse - Konstruktor Problem
-
Hallo, ich wollte mir eine "Menü Klasse" schreiben, aber es kommt da beim ersten Konstruktor schon immer ein Error, ich schätze mal das kommt irgendwie davon das ich das Array "P" im Konstruktor nicht richtig zuweise?
ERROR:
Compiling: main.cpp
In file included from main.cpp:2:
standard.h: In constructormenu::menu(std::string)': standard.h:29: error: expected(' before '[' token
standard.h:29: confused by earlier errors, bailing out
Process terminated with status 1 (0 minutes, 0 seconds)
2 errors, 0 warningsKLASSE "menu"
#ifndef STANDARD_H_INCLUDED #define STANDARD_H_INCLUDED #include <string> #include <iostream> using namespace std; class menu { public: 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); // } ~menu(); void Draw(int a); //überladen, um den Abstand der Menüpunkte anzugebe (1=1Zeile, 2=2Zeilen 3=3Zeilen....) void Draw(); //wenn man nichts angibt dann ist der Abstand automatisch 0 Zeilen und der Menüpunkt ist direkt unter //dem überliegenden private: string P[7]; const short NUMBER_OF_P; }; menu::menu(string P1) :P[0](P1),NUMBER_OF_P(1) { } menu::menu(string P1,string P2) :P[0](P1),P[1](P2),NUMBER_OF_P(2) { } menu::menu(string P1,string P2,string P3) :P[0](P1),P[1](P2),P[2](P3),NUMBER_OF_P(3) { } menu::menu(string P1,string P2,string P3,string P4) :P[0](P1),P[1](P2),P[2](P3),P[3](P4),NUMBER_OF_P(4) { } menu::menu(string P1,string P2,string P3,string P4,string P5) :P[0](P1),P[1](P2),P[2](P3),P[3](P4),P[4](P5),NUMBER_OF_P(5) { } menu::menu(string P1,string P2,string P3,string P4,string P5,string P6) :P[0](P1),P[1](P2),P[2](P3),P[3](P4),P[4](P5),P[5](P6),NUMBER_OF_P(6) { } menu::menu(string P1,string P2,string P3,string P4,string P5,string P6,string P7) :P[0](P1),P[1](P2),P[2](P3),P[3](P4),P[4](P5),P[5](P6),P[6](P7),NUMBER_OF_P(7) { } menu::~menu() { } void menu::Draw(int a); { for (int i=0;i<NUMBER_OF_P;i++) { cout << P[i] << "(" << i << ")\n"; for (int j=0;i<a;i++) { cout << "\n"; } } } void menu::Draw(); { for (int i=0;i<NUMBER_OF_P;i++) { cout << P[i] << "(" << i << ")\n"; } } #endif // STANDARD_H_INCLUDEDUnd dann hätte ich noch eine Frage, und zwar wie ist das den genau mit den Headern der STL. Wie man sieht habe ich jetzt in der Klasse "menu" den Header <iostream> und <string> eingebunden, und ich hab mir halt gedacht es kommen strings vor also muss ich wohl auch den "string Header" einbinden, genauso bei <iostream>, weil in der Funktion "void menu::Draw...." kommt ja ein "cout" vor somit muss man ja wohl oder übel <iostream> einbinden. So, aber muss ich nun eigentlich in der main nochmal <iostream> und <string> mit einbinden, oder wie läuft das? Wie macht ihr das mit euren Headern und so. Kann mir das jemand mal erklären?
Dankeschön schon mal im Voraus.
-
Alle deine Überladungen sind unsinnvoll und müssten weg.
-
Ganz böser Code
Mach das mal ein wenig anders... davon abgesehen das using namespace im header verboten sein sollte und du die ganzen implementierungen auch im header hast ... machs knackig und kurz:control_menu.h
#ifndef CONTROL_MENU_H__INCLUDED #define CONTROL_MENU_H__INCLUDED #pragma once #include <iostream> #include <string> #include <vector> namespace control { class menu { public: menu(); ~menu(); public: void draw(const unsigned short line_heigth = 0); size_t add_item(const std::string&); private: std::vector<std::string> m_items; }; }; #endif // CONTROL_MENU_H__INCLUDEDcontrol_menu.cpp
namespace control { menu::menu() { } menu::~menu() { m_items.clear(); } void menu::draw(const unsigned short line_heigth) { for (size_t item = 0; item < m_items.size(); ++item) { std::cout << "(" << (item + 1) << ") " << m_items[item]<< std::endl; for (unsigned short line_free = 0; line_free < line_heigth; ++line_free) std::cout << "\n" << std::flush; } } size_t menu::add_item(const std::string& item) { m_items.push_back(item); return (m_items.size() - 1); } }...

aja wie du es nutzt:
#include "control_menu.h" int main() { control::menu menu; menu.add_item("Exit"); menu.add_item("About"); menu.add_item("Calc"); menu.add_item("Settings"); menu.draw(); }Hmm warum ich namespaces direkt mit eingesetzt hab?
So setzt du nachher alle deine controls in den namespace "control" und verlierst nicht die Übersicht und es kann gleichzeitig nicht zu Namensüberschreibungen kommen.
-
... oben hinzu editiert ...
-
(D)Evil schrieb:
menu::~menu() { m_items.clear(); }wtf?
-
Ooh, meine güte seit ihr schnell

Hab in der zeit an einer anderen lösung getüftelt- hier kann der OP seinen riesenkonstructor verwenden, wenn er will, sieht etwa so aus (alles in einer .cpp datei :p )#include <stdio.h> #include <iostream> #include <stdarg.h> using namespace std; class MyMenu{ public: int iMenuOptionCount; //amount of strings in menu char** pMenuOption; //pointers to the strings of menu //constructor (_!!the sole and only ONE!!_ constructor) MyMenu(int iOptionCount, ...){ //saving the count of menu options iMenuOptionCount=iOptionCount; //allocating enough space on heap pMenuOption=new char* [iMenuOptionCount]; //initializing the list of variables //pointer to the list of args: va_list pArgList; //start is right after "iOptionCount" arguement: va_start (pArgList, iOptionCount); //saving char*-pointers into the array for(int i=0; i<iOptionCount; i++){ pMenuOption[i]=va_arg(pArgList, char*); } //end: clearing pointers, closing argument list va_end (pArgList); } ~MyMenu(){ delete[] pMenuOption; } void Draw(){ for(int i=0; i<iMenuOptionCount; i++){ cout<<pMenuOption[i]<<endl; } } }; int main(){ MyMenu menu=MyMenu(5, "open", "close", "save", "delete", "exit"); menu.Draw(); return 0; }würd einfach ma gerne hören, was es dran auszusetzen gibt

-
Jo und in C++ ist mein Vorschlag um 100% schöner. Und sicherer dazu auch noch.
-
Also das von Andrea funktioniert bei mir, bloß das verstehe ich so gar nicht:
Was sollen den bitte die "...", das hab ich noch nie wo gesehen:... MyMenu(int iOptionCount, ...) ...Uii

Äh und das von (D)Evil hat bei mir leider nicht funktioniert, da kamen bei mir ganz viele Erros:-------------- Build: Debug in menu_professional ---------------
Compiling: control_menu.cpp
control_menu.cpp:3: error:menu' has not been declared control_menu.cpp:4: error: ISO C++ forbids declaration ofmenu' with no type
control_menu.cpp:7: error: expected constructor, destructor, or type conversion before '::' token
control_menu.cpp:7: error: expected,' or;' before '::' token
control_menu.cpp:12: error:menu' is not a class or namespace control_menu.cpp: In functionvoid control::draw(short unsigned int)':
control_menu.cpp:14: error:size\_t' undeclared (first use this function) control_menu.cpp:14: error: (Each undeclared identifier is reported only once for each function it appears in.) control_menu.cpp:14: error: expected;' before "item"
control_menu.cpp:14: error:item' undeclared (first use this function) control\_menu.cpp:14: error:m_items' undeclared (first use this function)
control_menu.cpp:16: error: `cout' is not a member of `std'
control_menu.cpp:16: error: `endl' is not a member of `std'
control_menu.cpp:19: error: `cout' is not a member of `std'
control_menu.cpp:19: error: `flush' is not a member of `std'
control_menu.cpp: At global scope:
control_menu.cpp:23: error: `size_t' does not name a type
Process terminated with status 1 (0 minutes, 0 seconds)
15 errors, 0 warningsSo ich mach jetzt dann auch nochmal eine neu Klasse Menü, hab ja jetzt viele Anregungen von euch gesammelt. Das mit den riesieg überladenen Konstruktoren is glaub nicht so das Ware, und wir mit der Zeit sicherlich unnötig komplizert. Aber Andrey hat meine Idee schon irgendwie verwirklicht, aber ohne das man die hundertausende von Konstruktoren schreiben muss... mh ich hab zwar no net so ganz verstanden wir er das gemacht hat, aber werds mir gleich mal genau durchschauen.
-
1.) ich heisse Andrey (ist n männlicher name, damit hir ja nix missverstandn wird [warum haben alle probleme mit russischen namen
]
)
2.) Die "..." stehen für eine beliebige anzahl von argumenten: du kannst da beliebig viele char* argumente übergeben. Aber wie (D)Evil gesagt hat: sicher ist die sahce nicht, wenn du dich da vertippst oder verzählst, gibts mächtig stress und der compiler meldet nicht ma fehler
3.) die lösung von (D)Evil lässt sich auch für dynamisch veränderbare menus verwenden, bei meinem vorschlag müsste man wohl das ganze programm neucompilieren, um den inhalt des menus zu verändern
=> hör lieber auf (D)Evil
, ich joke hier nur n bissl rum 
-
@ 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_INCLUDEDstandard.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_INCLUDEDUnd "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 mussHalte ich für wesentlich einfacher, übersichtlicher und sicherer ...
Gruß,
Simon2.