Erzeugen von Objekten über Methode einer anderen Klasse und Pointerrückgabe



  • Hallo allerseits,

    nachdem ich eine Weile mit anderen Programmiersprachen gearbeitet habe, arbeite ich mich gerade in c++ ein. Was Pointer sind, habe ich zwar schon vor einer Weile verstanden (dachte ich^^), aber ich habe nun ein Problem, bei dem ich auf eure Hilfe hoffe.

    Ich habe in meinem Projekt eine Klasse "World" und eine Klasse "Block". Die Grundidee ist, dass meine "World"-Klasse die Block-Objekte verwaltet. Ich möchte in meiner main()-Funktion ein Objekt der World-Klasse aufrufen und ihm Parameter übergeben, sodass dann die World-Funktion mit diesen Parametern ein Block-Objekt erzeugt, auf das ich später auch von der Main-Funktion aus zugreifen kann.

    Den Namen für das Block-Objekt kann ich ja in dem Aufruf nicht als Variable übergeben, daher hatte ich das Ganze so gebastelt, dass meine aufgerufene world.createBlock-Funktion als Rückgabewert einen Pointer auf den erzeugten Block zurückgibt, um dann damit zu arbeiten. Aber da habe ich offenbar falsch gedacht, denn von dem Pointer aus komme ich nicht mehr so recht zurück auf das Objekt. Könnt ihr mir einen Tipp geben, wo ich falsch denke?

    main.cpp

    #include <iostream>
    	#include "World.h"
    	int main(){
    
    	World world;
    
    	Block * blockpointer = world.createBlock(10,12);
    	Block block = *blockpointer;
    
    	std::cout << "(Main) Werte: " << block.xposition << ", " << block.yposition;
    
    	return 0;
    	}
    

    World.h

    #include "Block.h"
    
    class World
    {
    public:
    	World(void);
    	~World(void);
    
    	Block * blocklist [64][40];
    	char taken[64][40];
    
    	Block * createBlock(int x, int y);
    
    };
    

    World.cpp

    #include "World.h"
    #include <iostream>
    
    World::World(void)
    {	// hier passiert noch etwas, das für das Problem unwichtig ist//
    }
    
    World::~World(void) { }
    
    Block * World::createBlock(int x, int y)
    {
    	Block NeuerBlock(x,y);
    	blocklist[x][y] = &NeuerBlock;
    	taken[x][y] = 'b';
    	return &NeuerBlock;
    }
    

    Block.h

    #pragma once
    
    class Block : public sf::Drawable
    {
    public:
    	Block(int x, int y);
    	~Block(void);
    
    	int xposition;
    	int yposition;
    	sf::Vector2f location;
    };
    

    Block.cpp

    #include "Block.h"
    #include "Person.h"
    
    Block::Block(int x, int y)
    {  
    	xposition = x;
    	yposition = y;
    
    	location.x = (float)16*x; //das brauch ich später noch, ist auch eher herumgespielt, also nicht wundern
    	location.y = (float)16*y;
    }
    
    Block::~Block(void)
    {
    }
    

    Wenn ich nun in der main()-Funktion die Werte ausgebe, erhalte ich nicht die Werte, die ich für das Block-Element eigentlich setzen wollte.

    Ich würde sehr gern das Konzept beibehalten, dass meine World-Klasse meine Blöcke verwaltet. (Habe ja noch Pläne damit)
    Gibt es eine Möglichkeit, das so zu machen, und diese Struktur in etwa beizubehalten?

    Ich bin dankbar für alle eure Hinweise 🙂

    Schönes Wochenende noch.


  • Mod

    In world.cpp erzeugst du einen funktionslokalen Block. du gibst einen Verweis auf diesen Block zurück. Nach Beenden der Funktion existiert der Block aber nicht mehr.
    Weiterhin erzeugst du in Zeile 8 der main.cpp eine Kopie des Objekts, auf die der Zeiger aus Zeile 7 zeigt (also einen ganz neuen Block). Ich denke, das ist dir nicht klar, dass das nicht mehr der gleiche Block ist, wie der, auf den der Zeiger zeigt.

    Insgesamt sind mir da doch sehr viele Pointer drin. Leute sagen zwar immer, C++ hätte Pointer drin, aber das sind die Leute, die kein C++ können (oder C++ für C halten). In C++ hat man nur sehr selten überhaupt Pointer (viel seltener als z.B. in Java, wo sie bloß besser versteckt sind), und wenn überhaupt, dann sind sie versteckt im Hintergrund.

    Gibt es einen Grund, warum die World die Blocks nicht direkt verwaltet, sondern nur Verweise darauf? Selbst wenn es einen Grund gibt: Wenn die Blocks zur World gehören, dann sollten das besitzende Pointer sein.



  • In der createBlock-Funktion gibt es das Objekt NeuerBlock nur bis zum Ende der createBlock-Funktion. Der Zeiger, den du speicherst, zeigt danach auf Müll. Lass das mit den Zeigern und der Speicherverwaltung einfach sein, ist unnötig kompliziert und ... unnötig.
    blocklist sollte ein vector<Block> sein und createBlock brauchst du nicht, dafür gibt es den Konstruktor von Block.



  • SeppJ:

    Ich denke, das ist dir nicht klar, dass das nicht mehr der gleiche Block ist, wie der, auf den der Zeiger zeigt.

    Als mir das klar geworden ist, wusste ich nicht mehr weiter und hab mich deshalb an euch gewandt. ^^

    Ich sehe, dass es so nicht funktioniert...
    Aber ich möchte später das ganze modular aufteilen, sodass die Verwaltung der "Welt" (also world) unabhängig von der eigentlichen main-Funktion läuft und für diese den Weltinhalt (Block) verwaltet.

    In meinem Projekt soll es in der Welt Elemente vom Typ Block geben, und dann später noch von Block abgeleitete Klassen, also eine Menge von Objekten, über die ein world-Objekt den Überblick behalten soll. Gibt es keine Möglichkeit, meinen Code in dieser Art zu strukturieren?
    Ich verzichte auch gerne auf die vielen Pointer, wenn es einen anderen Weg gibt. 🙂

    Edit:
    Also um das mal umgangssprachenlich irgendwie zu verdeutlichen:

    main() an world-Objekt: modifziereWorld()
    main() an world-Objekt: erzeugeBlock(und zwar mit diesen Parametern)
    world-Objekt an Block: erzeuge dich(und zwar so)!
    world-Objekt an main: Hab einen Block erzeugt.
    main an world: sag mir mal alle deine Blöcke.
    world an main: das hier sind meine Blöcke...

    so in der Art. ^^

    PPS: Nein, es gibt vermutlich keinen guten Grund, warum world die Blöcke nicht direkt verwaltet, außer dass mir keine gute Möglichkeit dazu eingefallen ist. 😕 Ich nehme an, das mit dem vector<Block> geht in die Richtung?



  • struct World{
    	std::vector<std::unique_ptr<Block>> blocklist;
    };
    
    int main(){
    	World world;
    	//world erzeuge block mit diesen parametern
    	world.blocklist.push_back(std::make_unique<Block>(8, 15));
    	//world sag mir deine parameter
    	for (const auto &b : world.blocklist)
    		std::cout << b->xposition << b->yposition;
    }
    


  • Hallo nochmal,

    ich werde nun wohl meine Implementation ganz über den Haufen werfen und nochmal anders herangehen. Danke für eure Hinweise, sie haben mich ein Stück aus der Sackgasse geholt.



  • SeppJ schrieb:

    In C++ hat man nur sehr selten überhaupt Pointer

    dann zeig' mir mal, wie du Qt in C++ ohne Pointer programmierst.

    SeppJ schrieb:

    Leute sagen zwar immer, C++ hätte Pointer drin, aber das sind die Leute, die kein C++ können (oder C++ für C halten).

    Satire bitte kennzeichnen. Oder meinst Du das etwa ernst ?

    Daß man ohne irgendeine Art Referenzen in C++ nicht auskommt, wirst Du wohl nicht bestreiten. Referenzen können Pointer oder eben references sein (unglückliche Bezeichnung in C++, da reference auch einen Oberbegriff meint, der Zeiger und Referenzen einschließt).

    Erstens kann eine Referenz intern durch einen Pointer realisiert sein (Maschinencode: indirekte Adressierung). Da haben wir also intern eventuell Pointer, die bei Verwendung implizit dereferenziert werden.

    Zweitens ist eine Referenz semantisch nicht das gleiche wie ein Zeiger. Referenzen lassen sich nicht nachträglich auf andere Objekte umleiten, müssen unmittelbar initialisiert werden, sind != 0 usw. Benötigt man aber genau solche Eigenschaften, braucht man ... na eben, Pointer.



  • großbuchstaben schrieb:

    Referenzen können Pointer oder eben references sein (unglückliche Bezeichnung in C++, da reference auch einen Oberbegriff meint, der Zeiger und Referenzen einschließt).

    Der Begriff Referenz ist in C++ ziemlich klar definiert als das, was du mit & deklarierst. Als Oberbegriff würde ich vielleicht eher "Verweis" oder "Indirektion" nehmen (auf Englisch wirds schwieriger, aber "reference" ist auch dort klar definiert).

    großbuchstaben schrieb:

    Erstens kann eine Referenz intern durch einen Pointer realisiert sein (Maschinencode: indirekte Adressierung). Da haben wir also intern eventuell Pointer, die bei Verwendung implizit dereferenziert werden.

    Ja, aber das ist ein Implementierungsdetail. Man benutzt deswegen keine Zeiger.

    großbuchstaben schrieb:

    Benötigt man aber genau solche Eigenschaften, braucht man ... na eben, Pointer.

    Da stimme ich völlig zu, auch in C++ sind Zeiger immer wieder anzutreffen. Zwar etwas seltener als in C, aber sie haben diverse Anwendungsbereiche. Ich denke, SeppJ hat das etwas überspitzt formuliert, weil es tatsächlich nicht wenige Leute gibt, die C in C++ programmieren. Wirklich schlimm wird es, wenn man dauernd mit rohen Zeigern Speicher verwaltet -- daran erkennt man relativ schnell, wer C++ verstanden hat.



  • Nexus schrieb:

    Der Begriff Referenz ist in C++ ziemlich klar definiert als das, was du mit & deklarierst. Als Oberbegriff würde ich vielleicht eher "Verweis" oder "Indirektion" nehmen (auf Englisch wirds schwieriger, aber "reference" ist auch dort klar definiert).

    eine C++-"reference" (&) ist ein Speziallfall des allgemeinen Begriffs "reference", der seinerseits viel älter ist als C++. Ein C++-"pointer" ist ein anderer Spezialfall.

    Generationen von Informatikstudenten lernen, was ein "call-by-reference" ist, in C++ aber kann ein "call-by-reference" sowohl einen C++-pointer als auch eine C++-reference meinen. Eine etwas unglückliche Wahl der Bezeichnung, würde ich meinen. Warum nicht "implicit pointer", "transparent pointer", "fixed pointer", "pointer-alias" oder irgendetwas Anderes anstelle von "reference".

    Nexus schrieb:

    Ja, aber das ist ein Implementierungsdetail. Man benutzt deswegen keine Zeiger.

    dann schau' Dir mal an, was der Compiler aus einer Referenz macht, und was er aus einem Zeiger macht. Stichwort indirekte Adressierung (auf CPU-Ebene).



  • großbuchstaben schrieb:

    Nexus schrieb:

    Ja, aber das ist ein Implementierungsdetail. Man benutzt deswegen keine Zeiger.

    dann schau' Dir mal an, was der Compiler aus einer Referenz macht, und was er aus einem Zeiger macht. Stichwort indirekte Adressierung (auf CPU-Ebene).

    Ihr diskutiert komplett aneinander vorbei.
    Nexus (und SeppJ) reden von der Ebene der Programmiersprache C++ und den abstrakten Konzepten Zeiger und Referenz.
    Du redest von der Implementierung auf Hardwareebene.
    Auf der Ebene von C++ ist eine Referenz eine Referenz und hat nichts mit einem Pointer zu tun. Auf deiner Ebene ist eine Referenz ein Pointer, nichtsdestotrotz verwendet man als Programmierer keinen Pointer, sondern eine Referenz.
    Und ich muss SeppJ definitiv zustimmen, rohe (besitzende) Pointer nutzt man in C++ wenn überhaupt nur stark abgekapselt.



  • Nathan schrieb:

    Ihr diskutiert komplett aneinander vorbei.

    glaube ich nicht. Die Behauptung war:

    Leute sagen zwar immer, C++ hätte Pointer drin, aber das sind die Leute, die kein C++ können (oder C++ für C halten).

    ich halte dies für Mumpitz.

    Nathan schrieb:

    Und ich muss SeppJ definitiv zustimmen, rohe (besitzende) Pointer nutzt man in C++ wenn überhaupt nur stark abgekapselt.

    wo hat seppj das gesagt?

    außerdem warte ich noch immer auf jemanden von der no-pointer-Fraktion, der mir erklärt, wie man Qt in C++ ohne pointer programmiert.

    Ich bin übrigens sehr dafür, C++ als eigenständige Sprache zu betrachten und sich meinetwegen lieber heute als morgen von C-"Altlasten" zu trennen, aber pointer sind nunmal kaum verzichtbar in einer Sprache mit vollem Hardwarezugriff.

    Das ändert sich auch nicht dadurch, daß man "pointer" mit gewissen syntaktischen und semantischen Unterschieden zu "references" macht. CPU-nahe Dinge wie pointer und I/O-Register direkt programmieren zu können ist eine der großen Stärken von C++, warum also die Bedeutung von Pointern verleugnen?



  • großbuchstaben schrieb:

    Generationen von Informatikstudenten lernen, was ein "call-by-reference" ist

    Und was haben diese Generationen von Informatikstudenden gelernt?
    Doch wohl, dass bei einem call-by-ref der Wert nicht kopiert wird, sondern Änderungen auch nach Ende der Funktion erhalten bleiben.
    Und genau das macht C++.
    In Pointer ist kein Call-by-ref (nebenbei genauso wenig, wie eine struct mit Pointern, die als Value übergeben wird, wäre nach deiner Argumentation ja dann auch call-by-ref).
    Einen Pointer kannst du als Refenence übergeben, aber ein Pointer als Value übergeben ist sicherlich kein Call-By-Ref.
    Neulich hatten wir die Diskussion im Java-Forum, weil da die Bezeichnung wirklich nicht ganz eindeutig ist (auch nach deiner Argumentation).
    Hier ist sie aber wohl so klar, wie sie klarer nicht sein kann.



  • großbuchstaben schrieb:

    Leute sagen zwar immer, C++ hätte Pointer drin, aber das sind die Leute, die kein C++ können (oder C++ für C halten).

    ich halte dies für Mumpitz.

    Wie gesagt, er redet vom Konzept "Pointer" in C++.
    Ein "Pointer" ist eine Variable die eine Adresse speichert und man auf den Wert, der an dieser Adresse steht zugreifen kann. Diese "Pointer" sind allerdings sehr fehleranfällig. Diese Fehler kann man mithilfe C++ Techniken vermeiden, weswegen man "Pointer" in C++ nur abgekapselt verwenden sollte.

    Nathan schrieb:

    Und ich muss SeppJ definitiv zustimmen, rohe (besitzende) Pointer nutzt man in C++ wenn überhaupt nur stark abgekapselt.

    wo hat seppj das gesagt?

    Mein Satz war so zu verstehen:

    Und ich muss SeppJ definitiv zustimmen.
    Rohe (besitzende) Pointer nutzt man in C++ wenn überhaupt nur stark abgekapselt.

    außerdem warte ich noch immer auf jemanden von der no-pointer-Fraktion, der mir erklärt, wie man Qt in C++ ohne pointer programmiert.

    Natürlich braucht man Referenzen (jetzt der Informatik, nicht der C++ Term) auf andere Objekte. Diese Referenzen sollten aber nicht rohe "Pointer" sein, sondern etwas besseres, z.B. "Smart Pointer" oder Handles.
    Und ich kenn Qt nicht, worauf spielst du an?

    Ich bin übrigens sehr dafür, C++ als eigenständige Sprache zu betrachten und sich meinetwegen lieber heute als morgen von C-"Altlasten" zu trennen, aber pointer sind nunmal kaum verzichtbar in einer Sprache mit vollem Hardwarezugriff.

    Ja, mit "Pointern" kann man viel mächtiges machen.

    Das ändert sich auch nicht dadurch, daß man "pointer" mit gewissen syntaktischen und semantischen Unterschieden zu "references" macht. CPU-nahe Dinge wie pointer und I/O-Register direkt programmieren zu können ist eine der großen Stärken von C++, warum also die Bedeutung von Pointern verleugnen?

    Wie gesagt, wir verleugnen nicht Pointer, sondern "Pointer".



  • großbuchstaben schrieb:

    Generationen von Informatikstudenten lernen, was ein "call-by-reference" ist, in C++ aber kann ein "call-by-reference" sowohl einen C++-pointer als auch eine C++-reference meinen.

    In Java hast du bis auf Basistypen nur Referenzen, aber nie Call by Reference.

    In C++ macht die strikte Trennung von Call-by-Value/Reference nur Sinn, wenn man Pointer-Übergabe als Call-by-Value interpretiert. Zeiger sind Objekte, Referenzen nicht.

    großbuchstaben schrieb:

    Eine etwas unglückliche Wahl der Bezeichnung, würde ich meinen. Warum nicht "implicit pointer", "transparent pointer", "fixed pointer", "pointer-alias" oder irgendetwas Anderes anstelle von "reference".

    Weil ein Begriff, der "Pointer" enthält, alles komplizierter macht, mehr Leute verwirrt und ständig unnötig lange Erklärungen erfordert. Stell dir nur mal vor, wie gewisse Typedefs in Containern aussähen.

    Da wir uns im C++-Kontext befinden, ist der Begriff jedem klar. Ich hatte noch nie das Problem, dass jemand mit Kenntnis der Sprache "Referenz" nicht verstand. Informatikstudenten lernen ein einziges Mal die Bedeutung von C++-Referenzen und wissen es dann.

    Was hingegen ein echtes Problem ist, sind "konstante" bzw. "nicht-konstante Referenzen". Das machen extrem viele Leute falsch.

    großbuchstaben schrieb:

    dann schau' Dir mal an, was der Compiler aus einer Referenz macht, und was er aus einem Zeiger macht. Stichwort indirekte Adressierung (auf CPU-Ebene).

    Eben: Implementierungsdetail, das hat den Anwender nicht zu interessieren. Genauso wie es den Anwender nicht interessieren muss, dass eine Schleife auf Assemblerebene durch Sprunganweisungen realisiert wird.

    Hochsprachen existieren zur Abstraktion von der Hardware. Es schadet zwar nicht, die Hintergründe zu kennen, aber aus Benutzersicht sind sie meist irrelevant.



  • SeppJ schrieb:

    Leute sagen zwar immer, C++ hätte Pointer drin, aber das sind die Leute, die kein C++ können (oder C++ für C halten).

    LOL 🙂

    lies mal den vorletzten Satz in der Antwort auf die Frage "Should I use call-by-value or call-by-reference" in der C++ Style and Technique FAQ vom C++ - Erfinder.



  • großbuchstaben schrieb:

    SeppJ schrieb:

    Leute sagen zwar immer, C++ hätte Pointer drin, aber das sind die Leute, die kein C++ können (oder C++ für C halten).

    LOL 🙂

    lies mal den vorletzten Satz in der Antwort auf die Frage "Should I use call-by-value or call-by-reference" in der C++ Style and Technique FAQ vom C++ - Erfinder.

    Meinste den?

    Stroustrup schrieb:

    My personal style is to use a pointer when I want to modify an object because in some contexts that makes it easier to spot that a modification is possible.

    Der nutzt das nicht, weil er Pointer haben will, sondern aus syntaktischen Gründen:

    f(a); //wird a jetzt verändert?
    g(&a) // ah, a wird höchstwahrscheinlich verändert
    

    Das Argument find ich allerdings nicht ganz so toll, man muss sich eh für vieles die Funktionssignatur anschauen. Ob die Funktion jetzt bspw. noexcept ist oder eine const-Memberfunktion sieht man ja auch nicht im Aufruf.


Log in to reply