Objektorientierte Klassenstruktur für Zeichenprogramm - Änfängerproblem
-
Hallo zusammen!
Obwohl die Lösung sicher denkbar einfach ist, fällt mir für ein einfaches Zeichenprogramm keine elegante objektorientierte Programmstruktur in C++ ein.
Als Beispiel seien folgende Klassen gegeben:
Klasse "Malwerkzeugkasten" (umfangreich!)
Klasse "Kreis"
Klasse "Dreieck"
Klasse "Rechteck"
...Das Problem besteht darin, daß der "Malwerkzeugkasten" viele Farben, Pinsel etc. enthält und deshlab von ihm zur Laufzeit nur 1 Instanz erzeugt werden soll!
Die Klassen "Kreis", "Dreieck" und "Rechteck" sollen einfachen Zugriff auf den "Malwerkzeugkasten haben und sich ihn teilen. Da der "Malwerkzeugkasten" sehr umfangreich ist, möchte ich verhindern, daß jede Klasse "Kreis", "Dreieck" etc. einen eigenen Malkasten besitzt.
Wie wird das am elegantesten objektorientiert in C++ programmiert?

Grüße,
Bernd
-
Wozu braucht denn ein Kreis Zugriff auf einen Malwerkzeugkasten?

Oder meinst du ein Kreismalwerkzeug? Wieder: Wozu muss das Werkzeug vom Werkzeugkasten wissen?
Dies ist kein C++-Problem, eher wirkt das Design an sich nicht gut durchdacht. In naivster Ausführung würde ich das so gestalten, das im Malwerkzeugkasten die Malwerkzeuge (Kreismalwerkzeug, Rechteckmalwerkzeug, usw.) sind, mit denen man dann wiederum die konkreten grafischen Figuren (also Kreise, Rechtecke, usw.) erzeugen kann.
-
Hallo SeppJ!
Danke für die Anwort. Die Klassen habe ich nur als Beispiel für ein grundsätzliches Problem beschreiben:
class CFarbe
{
public:
int Rot;
int Blau;
...
};class CPinsel
{
public:
void Zeichne();
int Dicke;
};void CPinsel::Zeichne()
{
Wie auf Farbe zugreifen???
}class CMalwerkzeug
{
public:
CFarbe Farben;
CPinsel Pinsel;
}Wie kann ich in der Funktion "Pinsel::Zeichne" auf "Farben" zugreifen?
Habe da wirklich eine Denkblockade.
Grüße,
Bernd
-
Das ist ja auch wieder komisch modelliert. Eine Farbe ist doch kein Werkzeug und die Pinselform ist eine Eigenschaft eines Werkzeugs, kein eigenes Werkzeug. So kennt man das ja auch von den "richtigen" Grafikprogrammen.
-
Habe meine Beispielklasse noch einmal geändert. Wie gesagt ist das nur ein Beispiel für eine grundsätzliche Frage: Wie wird hier in einer Funktion von "Kreis" auf "Pinsel1" zugegriffen?
Mein Verständnisproblem liegt glaube ich darin, daß ich nicht weiß, wie eine Klasse auf Datenelemente anderer Klassen zugreifen kann.
-
dein design scheint mir nicht gerade so gut gewählt zu sein, ich hab gerade mal
ein simples grundgerüst zusammengeschustert wie ich in einem einfachen projekt verwenden würde, kannste ja mal drübergucken
class CToolbox{ typedef unsigned char color; public: bool getColor(CColor& p_dest, color r, color g, color b, unsigned float a = 1.0f){ p_dest = CColor(r,g,b,a); return true; } bool getPinsel(CPinsel& p_dest, unsigned float p_size = 1.0f){ p_dest = CPinsel(p_size); } public: struct CColor{ color r, g, b; unsigned float a; CColor(color p_r = 0, color p_g = 0, color p_b = 0, unsigned float a = 1.0f){ r = p_r; g = p_g; b = p_b; a = p_a; } }; class CPinsel{ unsigned float size; CPinsel(){ size = 1.0f; } CPinsel(unsigned float p_size){ size = p_size; } }; public: class CCircleTool{ /*static*/ bool draw(const CColor& p_color, const CPinsel& p_pinsel, size_t p_radius){ /*draw the circle*/ } }; class CQuadTool{ /*static*/ bool draw(const CColor& p_color, const CPinsel& p_pinsel, size_t p_radius){ /*draw the quad*/ } }; class CTriangleTool{ /*static*/ bool draw(const CColor& p_color, const CPinsel& p_pinsel, size_t p_size){ /*draw the triangle*/ } }; };// c: CToolbox int main(){ CToolbox k; CToolbox::CColor c; CToolbox::CPinsel p; k.getColor(c,100,10,2,1.0f); k.getPinsel(p,3.75f); CToolbox::CCircleTool ct; ct.draw(c,p,50); CToolbox::CQuadTool qt; qt.draw(c,p,75); getchar(); return 0; }
-
Nicht böse gemeint, aber diese Präfixe stören absolut den Lesefluss
-
Hallo gamer804,
oh wow, danke für den Beispielcode!
Das muss ich mir erst einmal in Ruhe anschauen und geistig aufnehmen - bin wie gesagt noch Anfänger.
Danke und Grüße,
Bernd
-
Hallo gamer804,
oh wow, danke für den Beispielcode!
Das muss ich mir erst einmal in Ruhe anschauen und geistig aufnehmen - bin wie gesagt noch Anfänger.
Danke und Grüße,
Berndbitte, ist einfach eben schnell runtergetippt und nicht getestet, aber ich denke es ist leicht verständlich, wenn nicht, frag.

Ist nicht die beste lösung aber eine der einfachsten würde ich sagen, musste mal gucken ob das jetzt genau auf dein projekt passt, weil naja, hast ja nicht so viele infos gesagt.Nicht böse gemeint, aber diese Präfixe stören absolut den Lesefluss
Sorry, ich hab übrigens mich von euch überzeugen lassen und hab aus meinem gesamten aktuellen projekt die prefixe komplett entfernt 
ich dachte aber dieses mal mache ich es noch einmal, weil es dann einfacher zu verstehen (wenn auch nicht zu lesen) ist
-
@mireiner: Vergiss gamer8o4s Code am besten schnell wieder. Da drin sind so viel schlechter C++-Stil und Fehler, das schadet mehr als es nützt.
Nimm das bitte nicht persönlich, gamer8o4, aber auch abgesehen von den Präfixen ist der Code ein schlechtes Beispiel.
Zum Beispiel könntest du die Konstruktor-Initialisierungsliste verwenden. Und ein
color-Typedef für eine Farbkomponente (nicht Farbe) ist fragwürdig. Ebenso, wieso du einmalfloatund einmalunsigned charverwendest, und ebenso die Defaultparameter (bis auf den letzten). Der Konstruktor sähe besser so aus:Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255) : r(r) , g(g) , b(b) , a(a) { }Im Weiteren ist nicht klar, wozu
getColor()einenboolzurückgibt und eine Referenz als Parameter nimmt. Den Typenunsigned floatgibts erst gar nicht. Allespubliczu machen ist auch fragwürdig. Das/*static*/verwirrt nur.Nicht böse gemeint, aber mir scheint, du solltest auch noch ein wenig Grundlagen anschauen

-
Nimm das bitte nicht persönlich, gamer8o4, aber auch abgesehen von den Präfixen ist der Code ein schlechtes Beispiel.
Nein nein, ich bin selber noch ein halber anfänger

Aber hab trotzdem nochmal ein paar fragen zu deinen anmerkungen....du die Konstruktor-Initialisierungsliste verwendenso wie es ist kann man aber ganz einfach die getColor() funktion umschreiben um z.b. alle farben im farbkasten dunkler zu machen. das kann man nicht im konstruktor machen , weil man sonst die CColor objekte nicht mehr so gut zwischen verschiedenen farbkästen hin-und-her reichen könnte.
Und ein color-Typedef für eine Farbkomponente (nicht Farbe) ist fragwürdig.
okay, das ist echt dämlich

Ebenso, wieso du einmal float und einmal unsigned char verwendest
ich hab mir angewöhnt die sichtbarkeit als float zu speichern, weil sie dann genauer angeben kann und nicht mit werten zwischen 0 und 255 rumhantieren muss.
und ebenso die Defaultparameter (bis auf den letzten).
was gibt es an standartwerten auszusetzen? die hab nur genommen, damit ich keinen standartkonstruktor schreiben muss, der eine standartfarbe setzt.
wozu getColor() einen bool zurückgibt
sorry, hab ich vergessen zu ändern, die funktion sah erst anders aus

zurückgibt und eine Referenz als Parameter nimmt
warum soll man erst ein neues object von CColor erstellen, wenn man auch einfach ein vorhandenes nehmen kann? anders sehe der code so aus und das ist ineffizienter:
CColor c = getColor(100,10,75,1.0f);Alles public zu machen ist auch fragwürdig.
was willst du denn da private machen?
Nicht böse gemeint, aber mir scheint, du solltest auch noch ein wenig Grundlagen anschauen
bin immernoch halb dabei, auch wenn ich seit 2,5 jahren c++ programmiere

aber wie ich immer sage, mit 16 hab ich noch genug zeit das zu lernen
-
Ich würde hier auch sicher keine verschachtelten Klassen verwenden.
Im Grunde stell ich mir die Struktur in etwa so vor. Du hast deine Grafikobjekte, die von einer Basisklasse erben, z.B. DrawingItem mit den Ableitungen DrawingItemRect, DrawingItemCircle usw. Die haben Eigenschaften wie Koordinaten, Farbe usw. Dann gibt es entsprechende Factories, das sind die Tools, die man in der Toolbox auswählen kann. z.B. ein RectDrawingTool oder wie auch immer. Die Toolbox kann sie instanziieren und verwalten. Die brauchen widerum aber keinen Zugriff auf die Toolbox. Hier bin ich mir jetzt nicht sicher, weil ich nicht alle Anforderungen kenne und das ganze entsprechend nicht durchdacht ist. Aber die brauchen Zugriff auf die aktuellen Einstellungen. Ich würde es als Objekt unabhängig von der Toolbox modellieren. Vielleicht eine Klasse DrawingSettings, die auch Einstellugnen wie Farbe, Pinseldicke usw. hat. Die Tools bekommen eine Instanz übergeben. Die Toolbox verwaltet diese Instanz und kann die Eigenschaft ändern. Wenn du das Tool das nächste mal zum Zeichnen verwendest, hat es automatisch die aktuellen Einstellungen, die über die Toolbox gesetzt wurden.
-
Im Grunde stell ich mir die Struktur in etwa so vor. Du hast deine Grafikobjekte, die von einer Basisklasse erben, z.B. DrawingItem mit den Ableitungen DrawingItemRect, DrawingItemCircle usw. Die haben Eigenschaften wie Koordinaten, Farbe usw. Dann gibt es entsprechende Factories, das sind die Tools, die man in der Toolbox auswählen kann. z.B. ein RectDrawingTool oder wie auch immer. Die Toolbox kann sie instanziieren und verwalten. Die brauchen widerum aber keinen Zugriff auf die Toolbox. Hier bin ich mir jetzt nicht sicher, weil ich nicht alle Anforderungen kenne und das ganze entsprechend nicht durchdacht ist. Aber die brauchen Zugriff auf die aktuellen Einstellungen. Ich würde es als Objekt unabhängig von der Toolbox modellieren. Vielleicht eine Klasse DrawingSettings, die auch Einstellugnen wie Farbe, Pinseldicke usw. hat. Die Tools bekommen eine Instanz übergeben. Die Toolbox verwaltet diese Instanz und kann die Eigenschaft ändern. Wenn du das Tool das nächste mal zum Zeichnen verwendest, hat es automatisch die aktuellen Einstellungen, die über die Toolbox gesetzt wurden.
das ist schlau
aber ich glaube das wird an dieser stelle etwas kompliziert dann für den anfang oder?für ein einfaches Zeichenprogramm
-
Hallo zusammen!
Ich versuchte mein Problem anhand eines ganz simplen Beispiels zu erklären, was aber vermutlich mißlungen ist.
Konkret arbeite ich an einem MFC-Windowsprogramm mit GDI+ Grafik, das eine lange Reihe von Grafikresourcen bereitstellen muss:
Sehr verkürzte Darstellung! :
Pen *pen1;
Pen *pen2;
Pen *pen3;
Pen *pen4;SolidBrush *solidBrushSchwarz;
SolidBrush *solidBrushWeiss;
SolidBrush *solidBrushGrau;
SolidBrush *solidBrushHellgrau;Gdiplus::Font *fontSymbol;
Gdiplus::Font *fontSymbolText;
usw...Weil es sich um eine sehr lange Liste von Grafikresourcen handelt, möchte ich von ihnen zur Laufzeit nur 1 Instanz erzeugen.
An anderer Stelle im Programm habe ich dann viele Funktionen, die auf die obengenannten GDI+ Resourcen Zugriff haben müssen und zwar in der Regel auf fast alle gleichzeitig.
Wie wird der einfache Zugriff einer solchen Sammlung von Grafikresourcen nun am elegantesten in so ein Programm implementiert?
-
Wie wird der einfache Zugriff von Grafikresourcen nun am elegantesten in so ein Programm implementiert?
schreib dir einen kleinen GrafikManager der einen vector mit den grafikresourcen verwaltet (mit verwalten ist hier folgendes gemeint: zugriff ermöglichen, laden und löschen verwalten)
Konkret arbeite ich an einem MFC-Windowsprogramm mit GDI+ Grafik
dachte du bist anfänger? also ernstgemeinter tipp, wenn du wirklich noch ein anfänger bist, dann lass die finger von der MFC, da wäre nämlich ein wissen über generelle application designs insbesondere dem Doc-View-Design echt mehr als sinnvoll.. (hab selber böse erfahrungen gemacht, ich bin nämlich mit spieleprogrammierung eingestiegen ^^)
-
mireiner schrieb:
Wie wird der einfache Zugriff von Grafikresourcen nun am elegantesten in so ein Programm implementiert?
Jede Zeichenfunktion OnDraw eines Dialogs darf den Zeichenklasten kennen. Pro Dialog ein Zeiger auf den Zeichenkasten ist wohl ok. Und die aufgerufenen Funktioinen bekommen den Zeiger auf den Zeichenkasten halt mit.
-
Hallo volkard,
ah, mit einem Zeiger! Das ist glaube ich die Lösung auf die ich nicht gekommen bin.
Damit werde ich es erst einmal versuchen.
Danke und Grüße auch an alle anderen für ihre Ratschläge!
Bernd
-
gamer8o4 schrieb:
so wie es ist kann man aber ganz einfach die getColor() funktion umschreiben um z.b. alle farben im farbkasten dunkler zu machen. das kann man nicht im konstruktor machen , weil man sonst die CColor objekte nicht mehr so gut zwischen verschiedenen farbkästen hin-und-her reichen könnte.
Was hat das mit der Konstruktor-Initialisierungsliste zu tun?
Die
getColor()-Funktion ist komplett überflüssig, da man stattgetColor(c, r, g, b, a)immer auchc = Color(r, g, b, a)schreiben kann, was auch weniger verwirrend ist ("get" wird für anderes eingesetzt)...gamer8o4 schrieb:
ich hab mir angewöhnt die sichtbarkeit als float zu speichern, weil sie dann genauer angeben kann und nicht mit werten zwischen 0 und 255 rumhantieren muss.
Und für RGB gilt das nicht? Davon abgesehen wird die Grafikschnittstelle wahrscheinlich eh nur 256 verschiedene Alpha-Werte akzeptieren...
gamer8o4 schrieb:
was gibt es an standartwerten auszusetzen? die hab nur genommen, damit ich keinen standartkonstruktor schreiben muss, der eine standartfarbe setzt.
Dass man den Konstruktor mit 1 oder 2 Argumenten aufrufen kann, was nicht sinnvoll ist.
Color c(35, 100); // welche Komponenten sind nun gesetzt?! Color c = 200; // wtf!? auch möglich, da nicht explicitgamer8o4 schrieb:
warum soll man erst ein neues object von CColor erstellen, wenn man auch einfach ein vorhandenes nehmen kann? anders sehe der code so aus und das ist ineffizienter:
Es spielt genau gar keine Rolle, ob du das ganze Objekt oder dessen Member einzeln zuweist. Abgesehen davon sollte der Code zuerst einen vernünftigen Stil befolgen, bevor du an solche Mikrooptimierungen überhaupt denkst. Selbst wenn du hier 2 Nanosekunden herausholen solltest (was du nicht tust), wäre das komplett irrelevant, weil du andernorts viel mehr verbrätst. Was du hingegen nicht berücksichtigst, ist die Referenz und deren Dereferenzierung, was ohne Optimierung auch nicht gratis ist.
Fazit: Code erst schön schreiben. Nur schnell, wenn nötig.
gamer8o4 schrieb:
was willst du denn da private machen?
Ich würde gar keine Klasse machen, die nur andere enthält. Allenfalls ein Namensraum, aber hier kommt man gut ohne aus.
gamer8o4 schrieb:
aber wie ich immer sage, mit 16 hab ich noch genug zeit das zu lernen

Schau dir vielleicht auch ein gutes Buch wie den C++ Primer (nicht Primer Plus) an. Nur durch Programmieren lernt man -- gerade in C++ -- vieles nicht.
-
Mir ist gerade noch eine Idee gekommen, weiß aber nicht, ob sie möglich ist?
Eine Basisklasse mit einem Element "Baukasten", von dem zur Laufzeit nur 1 Instanz erzeugt wird und alle von dieser Basisklasse abgeleiteten Klassen können auf einfachem Wege auf diesen EINEN Baukasten der Basisklasse zugreifen.
Gibt es in C++ Sprachelemente die so etwas möglich machen?
Funktioniert das womöglich, wenn das Element "Baukasten" in der Basisklasse als statisch deklariert wird?
-
Ja,
staticermöglicht sowas. Führst du das weiter, kommst du zum Singleton-Pattern. Hier gabs gerade ne Diskussion darüber, warum das üblicherweise verpönt ist