DEUTSCHE VERSION |
|
Links | | | Forums | | | Comments | | | Report news |
Chat | | | Polls | | | Newsticker | | | Archive |
amiga-news.de Forum > Programmierung > G++ ld undefined Reference | [ - Search - New posts - Register - Login - ] |
-1- | [ - Post reply - ] |
2006-04-03, 21:06 h Reth Posts: 1858 User |
Hallo zusammen, versteh gerad nicht was da los ist. Ich hab ne Klasse B, von der inner anderen Klasse A ne Klassenvariable angelegt wird. Diese Klasse A kann eine Referenz auf die Klassenvariable zurückgeben. Ein Objekt der Klasse A wird per Referenz an eine Klasse C übergeben, dort wird dann von A per Methodenaufruf die Refernz auf B geholt und ne Methode von B gerufen. Die Klassen wurden prima compiliert und gelinkt, so lange ich in C nicht den Methodenaufruf von B implementiert hatte (über die Referenz von A). Die Klassen und Methoden waren alle schon da es wurde alles compiliert und gelinkt. Nun nachdem ich den Methodenaufruf doBStuff() in C implementiert hatte meldet der Linker beim letztendlichen Linken des Executables "Undefined Reference to B::getBStuff()" in C. Muss ich in C die Klasse B auch noch inkludieren? Wenn ja, wieso? Sorry, bekomme keine Leerzeichen in den Code, bei der Eingabe sind sie jedenfalls da. //Klasse A #include "B" class A { privat: B b; public: B& getB() { return b; } } ... // Klasse C #include A class C { public: void do(A& a) { a.getB().doBStuff(); } } Danke schon mal! Ciao [ - Answer - Quote - Direct link - ] |
2006-04-03, 22:03 h Reth Posts: 1858 User |
Es lag wohl an der Art der Implementierung von B, die auf Header und Source verteilt war und bei make nicht zusätzlich angegeben wurde. Habe das nun geändert und es sieht besser aus, habe aber noch kein endgültiges Ergebnis! Jop, das wars wohl! [ Dieser Beitrag wurde von Reth am 03.04.2006 um 22:47 Uhr geändert. ] [ - Answer - Quote - Direct link - ] |
2006-04-04, 08:22 h Solar Posts: 3680 User |
Mal abgesehen von den Problemen, die durch "make" erzeugt werden, wenn man das Makefile nicht ganz perfekt anlegt - irgendwie wundere ich mich doch sehr über das Design. Mir ist schon klar, das es hier um Beispielcode geht, aber eine nicht-konstante Referenz eines privaten Datenmembers aus einer Klasse herauszureichen, würde mich in "freier Wildbahn" zu etwa folgendem Dialog mit dem entsprechenden Programmierer veranlassen: Binde? Zigarette? Ein letzter Wunsch? [ - Answer - Quote - Direct link - ] |
2006-04-04, 08:45 h Reth Posts: 1858 User |
@Solar: Bin in C++ ziemlich blutiger Anfänger (obwohl das ja eine Designfrage ist und daher eigentlich von der Prog.-Sprache unabhängig). Aber wie würdest Du/"man" das dann designen? Im konkreten Fall hab ich eine Window-Klasse, die eine nicht konstante Referenz auf ihr RastPort-Objekt zurückgibt, damit man darin Operationen fahren kann. Wenn ich die Referenz konstant mache, muss ich alle Public-Methoden der RastPortklasse ebenfalls konstant machen, damit darauf von außen zugegriffen werden kann. Bin mir nicht sicher, ob das geht, da die RastPortklasse einen Zeiger auf ne RastPort-Struktur kapselt und darauf z.B. Blitoperationen macht und sich dabei ja die RastPort-Struktur ändert (allerdings nicht der Zeiger darauf, mit dem ja gearbeitet wird - geb ich zu). Wie erstellt man da ein Design, dass mit konstanten Referenzen arbeiten kann bzw. wie stellt man dann das Design um? [ - Answer - Quote - Direct link - ] |
2006-04-04, 09:03 h Solar Posts: 3680 User |
Zitat: Ich muß zugeben, das ich wenig bis gar keine Erfahrung mit GUI-Programmierung habe. Und im Moment bin ich nicht wirklich in der Laune, mich da reinzudenken. Vielleicht funktioniert's ja auch für dich, nur sauber ist es halt nicht. Evtl. magst Du Dir mal das keyword "mutable" anschauen, obwohl ich mir nicht sicher bin, ob das hier Sinn macht. [ - Answer - Quote - Direct link - ] |
2006-04-04, 09:21 h Reth Posts: 1858 User |
@Solar: Versteh ich, aber das hier genannte Problem ist ja nicht GUI-spezifisch. Ist halt nur zufällig so, dass ich bei der GUI-Programmierung drauf gestoßen bin. [ - Answer - Quote - Direct link - ] |
2006-04-04, 09:34 h Solar Posts: 3680 User |
Das Klassendesign läßt sich nie von der Anwendung trennen. Es geht ja darum, was Du wie abbilden mußt und worauf Du den Zugriff braucht usw. - es bringt ja nix, wenn ich Dir erzähle, mach dieses oder jenes const, aber Du kannst dann hinterher nicht damit arbeiten. So ein Murks wie oben weist meiner Erfahrung nach darauf hin, daß das Klassenmodell schlecht ist. Aufgrund meiner fehlenden Erfahrung fällt mir aber eben nicht auf die Schnelle ein besseres ein, und vielleicht "muß das so" in der GUI-Programmierung, was weiß ich. [ - Answer - Quote - Direct link - ] |
2006-04-04, 09:44 h Holger Posts: 8116 User |
Zitat:Gib eine Kopie des RastPort-Objekts zurück. mfg -- Good coders do not comment. What was hard to write should be hard to read too. [ - Answer - Quote - Direct link - ] |
2006-04-04, 09:58 h Reth Posts: 1858 User |
@Holger: Die Frage ist, ob das hier wirklich hilft? Das RastPort-Objekt ist um den Zeiger auf eine existierende RastPort-Struktur aufgebaut. Bei einer Kopie kann ich zwar den Zeiger auf diese RastPort-Struktur kopieren, aber das ändert ja nichts, die Zugriffsmöglichkeiten bleiben ja die gleichen (abgesehen von evtl. vorhandenen anderen Membern der RastPortklasse)! Die dahinterliegende RastPort-Struktur bleibt dieselbe (muss sie ja, da sie ja z.B. zu einem Fenster oder Screen gehören kann, in dem ich meine Sachen angezeigt haben will). Oder soll die RastPort-Struktur auch kopiert werden und jedesmal wenn eine Operation darauf ausgeführt wird der RastPort im Fenster, Screen etc. ausgetauscht werden? [ - Answer - Quote - Direct link - ] |
2006-04-04, 12:58 h DrNOP Posts: 4118 User |
@Reth: Ich hab' für objektorientierte Programmierung ausschließlich Schulwissen, aber ich dachte es wäre ein wesentlicher Wesenszug, daß nicht jeder von überall her irgendwie in allen Daten rumpfuschen darf? Insofern finde ich Holgers Vorschlag einleuchtend, weil dein Original-Rastport nur von der zugeordneten Klasse bearbeitet werden darf/kann. Dann braucht du eine Get-Methode und eine Set-Methode. Die Get-Methode gibt eine Kopie des Objekts zurück, mit dem der "Anfrager" dann nach Herzenslust jonglieren kann. Die Set-Methode bekommt auch eine Kopie des Objekts übergeben und trägt die Unterschiede in das Original ein oder ersetzt es gleich komplett. In deinem Fall gehören diese Methoden zu deiner Window-Klasse. -- Es gibt keine Notbremse für all den technischen Humbug, mit dem wir unsere Zeit vertrödeln. [ - Answer - Quote - Direct link - ] |
2006-04-04, 13:34 h Solar Posts: 3680 User |
get...() und set...() sind aber genausowenig "saubere" Lösungen. Nochmal, ich kenn' mich nicht aus, und weiß nicht einmal, was man mit einem RastPort so alles macht. Aber "normal" im Sinne des OOD wäre, wenn die Windows-Klasse Funktionen anbieten würde, die von Clients benutzt werden können - DrawLine() oder DrawRectangle() oder was auch immer - und diese Funktionen auf dem internen RastPort-Objekt umsetzen. Wenn Du so, wie oben gezeigt, interne Datenelemente unkontrolliert nach außen reichst, verlierst Du jede Kontrolle. [ - Answer - Quote - Direct link - ] |
2006-04-04, 14:54 h Reth Posts: 1858 User |
@Solar: Hi nochmal, genau diese Dinge bietet die RastPort-Klasse, nicht die Windowklasse, denn die genannten Operationen laufen auf einem RastPort und dieser ist unabhängig von einem Fenster (kann auch von einem Screen kommen, oder für sich existieren). Ist so ähnlich wie die Graphics-Klasse in Java. Es wird immer in einen RastPort gezeichnet, nicht in ein Fenster und nicht in einen Screen. Daher hat die Fensterklasse ein RastPort-Member. Dieses kann man sich besorgen, um dann die Zeichenoperationen darauf auszuführen. Wenn man will kann man auch ein eigenes RastPort-Objekt anlegen und darin herumzeichnen. Was man bei meiner Konstruktion allerdings (bisher) nicht kann (und auch erstmal nicht können soll), ist einem Fenster einen neuen RastPort zu setzen. [ - Answer - Quote - Direct link - ] |
2006-04-05, 12:36 h Holger Posts: 8116 User |
Zitat:Zeichenoperationen sind an sich nicht kritisch. Aber RastPort wie Graphics besitzen einen Zustand wie aktuelle Zeichenfarbe, Zeichenmodus, Line Pattern, etc. Man sollte sicherstellen, daß zu Beginn eines Refreshs ein definierter Ausgangszustand herrscht. Das kann man erreichen, in dem man immer eine Kopie resp. neuen RastPort herausgibt oder alle Attribute zurücksetzt. Von den Benutzern des Objekts zu erwarten, daß sie alle benötigten Attribute selber setzen (wie es teilweise z.B. in Swing erwartet wird), geht meist schief. Früher oder später wird ein neues Attribut eingeführt (wie z.B. Anti-Aliasing bei Graphics2D) und A spielt damit herum, während B dieses Attribut nicht kennt und deshalb gar nicht auf die Idee käme, es in einen definierten Ausgangszustand zu versetzen. Zitat: So oder so ist es eigentlich untypisch, daß verschiedene Entitäten parallel in ein und demselben Objekt (Fenster) herummalen. Normalerweise gibt es genau eine dafür zuständige Instanz, die dann für verschiedene Teilbereiche an andere Objekte delegiert. Deshalb ist eine getRastPort-Methode auf einem Fenster nicht sinnvoll. Dem Fenster sollte die fürs Zeichnen zuständige Instanz bekannt sein, an die es den Refresh delegieren kann. Vereinfacht so: C++ code:class Painter { virtual void paint(RastPortObj& rp); } class Window { //... bißchen was weggelassen ;) public: setPainter(Painter& p); protected: void refresh(); } Window::refresh() { windowPainter.paint(new RastPortObj(intuiWindow->RastPort)); } mfg -- Good coders do not comment. What was hard to write should be hard to read too. [ - Answer - Quote - Direct link - ] |
2006-04-05, 12:49 h Reth Posts: 1858 User |
Hi Holger,Zitat: Komme bei dem Bsp. nicht ganz mit. Wird da bei setPainter() im Window der internen Variable windowPainter die Referenz auf p zugewiesen, oder sind das 2 unterschiedliche Painterobjekte, die da im Spiel sind? In letzterem Fall komm ich gleich gar nicht mehr mit. Wie kommt man denn in obigem Bsp. an Blitfunktionen und so Sachen wie Vordergrund-/Hintergrundstift setzen dran? Momentan sind das Methoden meiner RastPortklasse, die public sind, da man ja die Darstellung beeinflussen will. Wird im Painterobjekt gesammelt, so lange, bis paint() gerufen wird? Vielleicht kannst Du mir hier kurz auf die Sprünge helfen?! Ciao [ - Answer - Quote - Direct link - ] |
2006-04-05, 19:45 h Holger Posts: 8116 User |
Zitat:Ne, ne, is schon so, wie Du denkst. setPainter() macht das, was Du von einer set... Methode erwarten würdest. Ala code:windowPainter=p; requestRefresh(); Zitat:Was willst Du sammeln? Du hast Objekte, die einen Status haben, und wenn der sich ändert mußt Du einen Refresh triggern, z.B. mit einer Methode ala requestRefresh(); auf dem Fenster (die Du natürlich schreiben mußt...), funktional vergleichbar mit repaint() in Java. Optional sollte man auch einen Bereich übergeben können, der dann zur damagedRegion des Layers hinzugefügt werden kann. Das ist in dem anderen Thread ja schon durchgekaut worden. Wenn Du den painter so implementierst, daß er immer alles neuzeichnest, dafür vor dem refresh aber die clip-regionen auf den betroffenen Bereich setzt, bist Du im Prinzip schon fertig. Das funktioniert dann eben auch, wenn ein fremdes Fenster über Deinem verschoben wurde. mfg -- Good coders do not comment. What was hard to write should be hard to read too. [ - Answer - Quote - Direct link - ] |
2006-04-05, 20:34 h Reth Posts: 1858 User |
Zitat:Gut, dachte schon ich blicks echt nich mehr! Zitat: Hm, ich seh gerad nur nicht, wo die letztendliche Implementation der Blitroutinen sein soll? Im Painter oder im RastPort, oder delegiert der Painter an den RastPort, wenn er was neu zu zeichnen hat? Also: code:painter.blit(...); window.requestRefresh(); ... // im Painter rastPort.blit(...); [ - Answer - Quote - Direct link - ] |
2006-04-05, 20:47 h Holger Posts: 8116 User |
Zitat:Was verstehst Du unter blit() ? So etwas wie Graphics.drawImage()? Dann weißt Du doch auch, wo es hingehört. In Deine RastPort-Kapselung. Der Painter kann es dann aufrufen. C++ code:class MainWindowPainter:public Painter { Image* background; Painter statusBarPainter; Painter levelPainter; void paint(RastPortObj& rp) { rp.blit(background, 0, 0); statusBarPainter.paint(rp); levelPainter.paint(rp); } } MainWindowPainter mainWindowPainter; window.setPainter(mainWindowPainter); //... //... //change background mainWindowPainter.background=newImage; //change status bar statusBarPainter.message="Watch the new background"; //and update display window.requestRefresh(); Hoffe, die C++-Syntax, die ich mir eh nicht merken kann, nicht allzusehr verhunzt zu haben. Und das ganz unten ist natürlich nicht sauber. Aber ich wollte nicht ne andere Baustelle aufmachen. mfg -- Good coders do not comment. What was hard to write should be hard to read too. [ Dieser Beitrag wurde von Holger am 05.04.2006 um 20:47 Uhr geändert. ] [ - Answer - Quote - Direct link - ] |
2006-04-05, 22:05 h Reth Posts: 1858 User |
@Holger: Jetzt versteh ich schon eher wie Du das mit den Paintern meinst. Aber wie mach ich dass, wenn ich viele Bilder blitten/drawen will, sollte das doch auch mit einem Refresh gehen. Sollte es dann einen Painter geben, der z.B. einen Vector mit Bildern entgegennimmt und diese dann blittet? Oder wie ist das in Deinem Konzept vorgesehen? Jedesmal das Image setzen und requestRefresh rufen kann ich mir nicht vorstellen! [ - Answer - Quote - Direct link - ] |
2006-04-05, 22:25 h Holger Posts: 8116 User |
Zitat:Ganz wie Du es brauchst. Du willst ja am Ende ein ganzes Level zeichnen. Der Painter, den Du auf dem Fenster setzt, soll den gesamten Inhalt zeichnen. Dazu kann er natürlich an andere Painter delegieren, die einzelne sichtbare Objekte kapseln. Dein tile-Hintergrund, die beweglichen Objekte, etc. Dann bewegst Du ein Objekt, in dem Du die Koordinaten eines Objekts änderst und das Fenster zum Refresh aufforderst. Optional kann man beim Refresh noch die betroffene Region angeben, die Fläche an der alten Position des Objekts und an der neuen. So kann man auch mehrere Objekte gleichzeitig bewegen, und dann nur einen Refresh auslösen. Ich denke, Du hast diese Objekte schon längst. Du mußt ihnen nur noch die einheitliche paint(...) Methode geben, um das "Painter-interface" zu erfüllen. mfg -- Good coders do not comment. What was hard to write should be hard to read too. [ - Answer - Quote - Direct link - ] |
2006-04-06, 08:13 h Reth Posts: 1858 User |
@Holger: Hm, dann würden bei mir aber für jedes Tile und jedes bewegliche Objekt ein eigener Painter entstehen, wenn ich den Objekten das Painter-Interface zuordne. Ist das nicht ein bisschen viel? Dachte eher daran, dass ein Painter (oder wie bei mir bisher ein RastPort-Objekt) alles macht, ähnlich dem Graphics-Objekt von Java-Components (das kann man ja auch nicht setzen). Der Statuspainter würde dann z.B. in meinem Fall ein Array(Liste) aus Strukturen (Objekten) übergeben bekommen mit Texten und Positionen dazu, um die Ressourceninformationen zu schreiben. Du merkst sicher schon, dass ich mich mit dem Ansatz etwas schwer tue, obwohl er mir gefällt. [ - Answer - Quote - Direct link - ] |
2006-04-06, 13:50 h Holger Posts: 8116 User |
Zitat:Nein, erzeuge einfach exakt so viele Objekte, wie Du es jetzt auch schon tust. Ist jedes tile ein einzelnes Objekt -> ok, dann ist jedes tile auch ein eigenständiger painter. Gibt es kein tile Objekt, sondern ein Background-Objekt, das alle tiles als array o.ä. verwaltet -> ok dann wird dieses Background-Objekt zum painter. Hast Du nur ein großes Mammut-Objekt, das das gesamte Spiel-Level repräsentiert (nicht sehr empfehlenswert) -> dann kann auch dieses Mammut-Objekt ein Painter sein, der das gesamte Level zeichnet. Zitat:Irgendwie wirfst Du immer noch durcheinander, wer etwas "macht". Das Graphics-Objekt, das dem RasPortObj entspricht, "macht" nichts. Es stellt Methoden zur Verfügung, mit denen man zeichnen, malen, blitten, wie auch immer kann. Und dann gibt es da noch jemanden, der diese Methoden aufrufen muß. Und der ist auch derjenige, der das Wissen hat, was dabei herauskommen soll. Unter AWT überschreiben Gadget- (Component) Unterklassen die paint() oder update()-Methode und dort "machen" sie dann, in dem sie Methoden auf dem Graphics-Objekt aufrufen, das sie übergeben bekommen. Swing-Objekte dagegen delegieren an ein "UI" Objekt, JButton z.B. an ein ButtonUI-Objekt. Neben der Eingabeverarbeitung sind die UI-Objekte auch für's Zeichnen zuständig, sie entsprechend in diesem Punkt dem Painter. Genauso, wie man im Beispielcode einen Painter auf dem Window setzen kann, kann man auf einer Swing-Komponente das UI-Objekt setzen. Das passiert im Normalfall aber nur exakt einmal innerhalb einer Anwendung. (Und passiert auch, ohne daß man sich drum kümmern muß). So soll auch auf dem Window auch nur in Ausnahmefällen ein anderer Painter gesetzt werden. Normalerweise stellst Du ja immer das Gleiche dar, ein Level, eine Statusanzeige, ein paar Konfigurationsmögichkeiten, etc. Nur der Zustand dieser Objekte ändert sich. Und wenn das passiert, wird lediglich dem Fenster mitgeteilt, daß ein Refresh gewünscht ist. Der Painter bleibt derselbe. Zitat:Das ist vollkommen ok. Man muß immer sorgfältig auswählen, welchen Objekten man die Fähigkeit, sich selbst zu zeichnen, gibt und welche von einem anderen Objekt (hier: einem bestimmter Painter) gezeichnet werden sollen. Das ist zum einen Erfahrungssache, zum anderen ist es aber auch nicht schlimm, wenn man die Grenze nicht optimal zieht. mfg -- Good coders do not comment. What was hard to write should be hard to read too. [ - Answer - Quote - Direct link - ] |
2006-04-06, 20:39 h Reth Posts: 1858 User |
Zitat: Hm, dann hab ich wohl den Waal. Meine Animationsobjekte haben ihre Position und bekommen von außen ggf. neue Positionen gesetzt, sie alle werden aber in einem anderen Objekt verwaltet, welches über die Änderungen wachen soll und diese zu Bild bringen. Allerdings wollte ich die gesamte Animationshierarchie entkoppelt lassen von zeichenspezifischen Sachen, was sich ja aufhebt, wenn ich einer dieser Klassen das Painter-Interface verpasse. Zitat: Wenn aber z.B. jedes Animationsobjekt einen Painter darstellt ist es doch eher "übel" diesen im Window zu setzen, dann wäre es doch eher angebracht diese alle in den Layoutmanager zu hängen und der ruft dann die paint()-Methoden auf, wenn er ein Update vom Fenster mitgeteilt bekommt?! Ciao [ - Answer - Quote - Direct link - ] |
2006-04-07, 11:31 h Reth Posts: 1858 User |
Da wir eh schon OT sind, fällt mir gleich noch ne Frage in diese Richtung ein? Wo platziere ich am dümmsten die Warteschleife auf messages? Wenn ich mir eine Receiverklasse(nhierarchie) baue, welche z.B. auf INTUITION-Messages wartet, könnte ich den Loop z.B. in dieser unterbringen: Pseudocode: C++ code:IntuiMSGReceiver::receive(struct MsgPort *Port) { struct IntuiMessage *msg = (struct IntuiMessage *) GetMsg(Port); // reply und verarbeiten } Da fallen mir noch spontan 2 Ansätze ein: 1. Eine Abstrakte Receiverklasse mit einer virtuellen Receive-Methode (quasi das Interface). 2. Eine (evtl. virtuelle) Receiverklasse, die den Empfang und den REply der Message schon implementiert hat, abgeleitete Klassen müssen den MessageTyp in Abhängigkeit ihrer Aufgabe (IntuiMessage) noch entsprechen umwandeln. [ - Answer - Quote - Direct link - ] |
-1- | [ - Post reply - ] |
amiga-news.de Forum > Programmierung > G++ ld undefined Reference | [ - Search - New posts - Register - Login - ] |
Masthead |
Privacy policy |
Netiquette |
Advertising |
Contact
Copyright © 1998-2024 by amiga-news.de - all rights reserved. |