Verstehe folgende Klassendefinition nicht



  • Hi,

    die folgende Klassendefinition in der Headerdatei ist mir an einigen Stellen unklar.

    class Vector {
    public:
    	Vector() { c[0] = c[1] = c[2] = 0.0; }
    	void normalize();
    	double &x() { return c[0]; }
    	double &y() { return c[1]; }
    	double &z() { return c[2]; }
    	const double *ptr() const { return c;}
    private:
    	double c[3];
    };
    
    1. Ich verstehe die Deklaration "double &x()" nicht. Genau genommen ist es das "&" was mich irritiert. Wenn ein Pointer deklariert wird dann wird doch der Stern verwendet, also etwa "double* x()" 😕

    2)Ich nehme an "x()" ist eine Funktion. Aber ich kann im Quelltext eine Zuweisung schreiben "myvector.x() = 123" und auch den wert abfragen "temp = myvector.x()".
    Wo findet denn die Zuweisung statt? In der Definition von "x()" steht doch nur die Abfrage des Arrayelementes "return c[0];", wo ist da die Zuweisung?

    3)Mit der Zeile "const double *ptr() const { return c;}" kann ich überhaupt nichts anfangen. Wofür stehen die beiden "const" und was hat die Pointer-Deklaration für einen Sinn? Ist das ein Funktionszeiger? Wenn ja, wofür brauch man den da.
    Ich hab bisher nur mit Variablen-Pointern gearbeitet. Über Funktionszeiger hab ich nur mal entfernt was gelesen.

    4)Wenn ich ein Exemplar der Klasse erzeuge, kommt ein Fehler wenn ich schreibe: "Vector myvector = new Vector();" 😕
    Es funktioniert wenn ich schreibe "Vector myvector;" und dann kann ich einfach zugreifen "myvetor.x() = ..." usw.. Warum muss ich nicht mit "new Vector()" den Konstruktor aufrufen und Speicher allokieren? 😕

    Viele Fragen zu einer kleinen Klasse, ich hoffe mir kann jemand weiterhelfen 🙂

    Gruß
    goforgold



  • zu 1.) Hier wird eine Funktion x() definiert, die als Rückgabewert eine Referenz-auf-double hat. Eine Referenz ist in C++ sowas wie ein "verbesserter" Zeiger, der unter anderem die Eigenschaft hat, einen Zugriff auf das referenzierte Etwas zu erlauben, ohne dereferenzieren zu müssen (via *).

    zu 2.) x() liefert eine Referenz auf c[0], anders gesagt x() liefert ein Alias für c[0], einfach nur einen anderen Namen. Dadurch bewirkt eine Zuweisung an x() in Wirklichkeit eine Zuweisung an c[0].

    zu 3.) Die Funktion ptr() gibt einen Zeiger auf double zurück (nicht ganz aber dazu später). Dieser Zeiger kann als Adresse auf ein Array interpretiert werden, damit bekommst Du also einen Zeiger auf alle drei Werte. Das erste const verändert den Typ double, so dass er nicht veränderbar ist (sprich: Du hast einen Zeiger auf drei const Doubles, also Werte die Du nicht verändern darfst). Das const hinten verändert die Funktion ptr() so, dass das Objekt vom Typ Vector, welches in main vor dem . steht konstant sein darf. Das heisst, Du kannst in main auch ein const Vector definieren und diese Funktion aufrufen.

    zu 4.) Mit der Definition "Vector myvector" definierst Du ein Objekt auf dem Stack, das bereits vollständig initialisiert ist, und am Ende der Funktion freigegeben wird. new liefert einen Zeiger auf den Typ zurück, korrekt wäre also höchstens "Vector * myvector = new Vector...". Aber dann kannst Du nicht mehr mit . auf die Elemente zugreifen und musst hinterher auch selbst mit delete wieder aufräumen.



  • Hi! Thx für die schnelle Antwort.

    1. und 2) verstehe ich jetzt. Bei 3) und 4) hab ich noch ein paar Schwierigkeiten.

    LordJaxom schrieb:

    zu 3.) Die Funktion ptr() gibt einen Zeiger auf double zurück (nicht ganz aber dazu später). Dieser Zeiger kann als Adresse auf ein Array interpretiert werden, damit bekommst Du also einen Zeiger auf alle drei Werte.

    "double*" kann auch als ein Zeiger auf ein double-Array verwendet werden? Ich dachte immer das müsste auch explizit angegeben werden, dass es sich um einen Zeiger auf ein Array handelt. 😕
    Also etwa "double*[]", weiß jetzt nicht mehr genau wie es war, aber ich meine in C ging das so.

    LordJaxom schrieb:

    Das const hinten verändert die Funktion ptr() so, dass das Objekt vom Typ Vector, welches in main vor dem . steht konstant sein darf. Das heisst, Du kannst in main auch ein const Vector definieren und diese Funktion aufrufen.

    was heißt "const Vector" definieren? bzw. was heißt ein Vector-Objekt darf konstant sein?

    LordJaxom schrieb:

    zu 4.) Mit der Definition "Vector myvector" definierst Du ein Objekt auf dem Stack, das bereits vollständig initialisiert ist, und am Ende der Funktion freigegeben wird.

    am Ende welcher Funktion?

    LordJaxom schrieb:

    new liefert einen Zeiger auf den Typ zurück, korrekt wäre also höchstens "Vector * myvector = new Vector...". Aber dann kannst Du nicht mehr mit . auf die Elemente zugreifen und musst hinterher auch selbst mit delete wieder aufräumen.

    Bringe ich da etwas durcheinander oder ist das Richtig, dass ein Exemplar einer Klasse 'normalerweise' mit "Vector myvector = new Vector()" erzeugt wird? Der Konstruktor muss doch aufgerufen werden?!



  • Für x vom Typ T* gilt, dass *(x+n) das gleiche macht wie x[n], darum gibt man ein Pointer auf das erste Element zurück und kanns "wie ein Array" verwenden.



  • goforgold99 schrieb:

    "double*" kann auch als ein Zeiger auf ein double-Array verwendet werden? Ich dachte immer das müsste auch explizit angegeben werden, dass es sich um einen Zeiger auf ein Array handelt. 😕
    Also etwa "double*[]", weiß jetzt nicht mehr genau wie es war, aber ich meine in C ging das so.

    Da musst Du was verwechseln, in C ist das genauso wie in C++. Ein Zeiger ist ja eine Darstellung für eine Position im Speicher. Da man Zeiger hoch- und runterzählen kann, kann ein Zeiger aber nicht nur auf eine Position zeigen, sondern auch auf die erste von vielen hintereinander angeordneten. Darüberhinaus kannst Du einen Zeiger wie ein Array benutzen sowie ein Array implizit in einen Zeiger umwandeln.

    double c[3];
    double* d = c; // implizite Umwandlung, d zeigt jetzt auf das erste von drei doubles
    cout << *d; // Ausgeben des doubles, auf das d zeigt oder
    cout << d[2]; // Ausgeben des dritten doubles, ausgehend von dem auf das d zeigt
    

    goforgold99 schrieb:

    was heißt "const Vector" definieren? bzw. was heißt ein Vector-Objekt darf konstant sein?

    Ein Code sagt mehr als tausend Worte:

    Vector const myvector;
    myvector.x() = 12; // illegal, weil myvector konstant ist, x() aber nicht
    double const* d = myvector.ptr(); // legal, weil ptr() hinten das const hat
    

    goforgold99 schrieb:

    LordJaxom schrieb:

    zu 4.) Mit der Definition "Vector myvector" definierst Du ein Objekt auf dem Stack, das bereits vollständig initialisiert ist, und am Ende der Funktion freigegeben wird.

    am Ende welcher Funktion?

    Am Ende derer Funktion, in der myvector definiert wurde, hier also main. Genaugenommen "lebt" ein solches Objekt in Funktionen bis zum nächsten "}" der aktuellen Ebene.

    goforgold99 schrieb:

    Bringe ich da etwas durcheinander oder ist das Richtig, dass ein Exemplar einer Klasse 'normalerweise' mit "Vector myvector = new Vector()" erzeugt wird? Der Konstruktor muss doch aufgerufen werden?!

    Der wird bei der Definition bereits aufgerufen. Und wie schon erwähnt gibt new Vector kein Vector zurück, sondern ein Vector*. Damit ist die Zuweisung illegal.

    int main()
    {
      Vector myvector; // definiert ein vollständig konstruiertes Objekt vom Typ Vector hier und jetzt auf dem Stack von main
    
      Vector * dynvector = new Vector; // definiert einen Zeiger auf einen Vector, dem sofort die Adresse eines neuen Objekts zugewiesen wird.
                                       // dieses lebt jetzt NICHT nur bis zum Ende der Funktion, sondern bis Du es selbst löscht:
      delete dynvector;
    
    } // Hier wird myvector automatisch zerstört
    


  • Enten wieder gewickelt schrieb:

    Für x vom Typ T* gilt, dass *(x+n) das gleiche macht wie x[n], darum gibt man ein Pointer auf das erste Element zurück und kanns "wie ein Array" verwenden.

    LordJaxom schrieb:

    Da musst Du was verwechseln, in C ist das genauso wie in C++. Ein Zeiger ist ja eine Darstellung für eine Position im Speicher. Da man Zeiger hoch- und runterzählen kann, kann ein Zeiger aber nicht nur auf eine Position zeigen, sondern auch auf die erste von vielen hintereinander angeordneten. Darüberhinaus kannst Du einen Zeiger wie ein Array benutzen sowie ein Array implizit in einen Zeiger umwandeln.

    stimmt. jetzt kommt es mir langsam wieder in Erinnerung.
    Hätte man in der Definion von

    const double *ptr() const { return c;}
    

    auch schreiben können "return &c[0]" ? Also die Adresse vom ersten Element zurückgeben.

    LordJaxom schrieb:

    Ein Code sagt mehr als tausend Worte:

    Vector const myvector;
    myvector.x() = 12; // illegal, weil myvector konstant ist, x() aber nicht
    double const* d = myvector.ptr(); // legal, weil ptr() hinten das const hat
    

    Das heißt in dem Fall, dass ich myvector als "Vector const" 'konstant' deklariere, darf ich alle methoden die kein "const" angehängt haben nicht verwenden?
    Ich habe nämlich gerade versucht einen Wert mit "x()" abzufragen: "double temp = myvector.x();", funktioniert aber nicht.
    Intuitiv hätte ich jetzt gesagt es klappt, da "const" für mich bedeutet, man darf nicht schreiben, jedoch lesen.

    Dann habe ich noch versucht aus der Deklaration

    const double *ptr() const { return c;}
    

    das erste "const" wegzunehmen, damit ich Werte ins Array schreiben kann, welches mir "*ptr()" zurückliefert.
    Also:

    double *ptr() const { return c;}
    

    Aber da kompilert er auch nicht.
    Fehler: "error C2440: 'return': 'const double [3]' kann nicht in 'double *' konvertiert werden"

    Heißt das, eine Methode mit angehängtem "const", muss auch immer so deklariert werden, dass am Anfang ein "const" steht, vor dem zurückliefernden Datentyp?

    Und allgemein: Welchen Sinn macht für mich die Deklaration als "Vector const myvector;" im Vergleich zu "Vector myvector;"?
    Dass ich nicht versehentlich etwas ins Array schreibe?

    Die Sache mit der "new" deklaration ist mir jetzt klar, danke!



  • goforgold99 schrieb:

    Hätte man in der Definion von

    const double *ptr() const { return c;}
    

    auch schreiben können "return &c[0]" ? Also die Adresse vom ersten Element zurückgeben.

    Ja

    goforgold99 schrieb:

    Das heißt in dem Fall, dass ich myvector als "Vector const" 'konstant' deklariere, darf ich alle methoden die kein "const" angehängt haben nicht verwenden?
    Ich habe nämlich gerade versucht einen Wert mit "x()" abzufragen: "double temp = myvector.x();", funktioniert aber nicht.
    Intuitiv hätte ich jetzt gesagt es klappt, da "const" für mich bedeutet, man darf nicht schreiben, jedoch lesen.

    Const bedeutet in dem Fall, dass das Objekt, was man innerhalb der Klasse als "this" kennt nicht beschrieben werden darf. Da die Funktion x() nicht const ist, bedeutet das für den Compiler "diese Funktion könnte Daten innerhalb des Objekts ändern". Da myvector hier im Beispiel const ist, darfst Du x() deshalb nicht aufrufen. x() könnte ja auch so aussehen:

    double& x() { c[0] = 36.4; return c[0]; }
    

    goforgold99 schrieb:

    Aber da kompilert er auch nicht.
    Fehler: "error C2440: 'return': 'const double [3]' kann nicht in 'double *' konvertiert werden"

    Da wenn die Funktion const markiert ist auch "this" const ist (siehe oben), sind auch alle Mitglieder von "this" const. Und Du versuchst jetzt das konstante Array c in einen Zeiger auf veränderbare (nichtkonstante) Werte umzuwandeln. Das nimmt der Compiler Dir natürlich übel.

    goforgold99 schrieb:

    Heißt das, eine Methode mit angehängtem "const", muss auch immer so deklariert werden, dass am Anfang ein "const" steht, vor dem zurückliefernden Datentyp?

    Nicht immer, aber immer wenn Du eine Referenz oder einen Zeiger auf ein in der Klasse definiertes Element zurückgibst.

    goforgold99 schrieb:

    Und allgemein: Welchen Sinn macht für mich die Deklaration als "Vector const myvector;" im Vergleich zu "Vector myvector;"?
    Dass ich nicht versehentlich etwas ins Array schreibe?

    Dass Du nicht versehentlich etwas änderst, was Du nicht ändern möchtest. Objekte sind eigentlich selten const (abgesehen von integralen Konstanten), aber Zeiger und Referenzen werden oft als const übergeben, weil man sie einerseits nicht kopieren möchte aber andererseits garantieren will, dass sie nicht verändert werden. Darüberhinaus sind auch temporäre Objekte immer const, so dass Du auch hier nur Funktionen benutzen kannst, die es auch sind.



  • Das leuchtet mir ein.
    Danke für die Hilfe! 🙂

    Gruß
    goforgold


Anmelden zum Antworten