copy constructor in abstraker Klasse definieren?



  • Hio,

    Ich habe eine abstrakte Basisklasse, von der später mehrere Klassen erben sollen. Im Programmcode sind alle Typangaben vom Typ der Basisklasse, sodass ich recht generisch programmieren kann. Jedoch brauche ich an einigen Stellen einen copy-C'tor (bzw. wäre es allgemein schön eine einheitliche Schnittstelle für die Konstruktoren der Oberklassen zu haben). Daher dachte ich mir, ich definiere einen c'tor in der Basisklasse, aber das nimmt visual-c++ nicht:

    class Position3D {
    	public:
    		Position3D(Position3D pPos) {
    			setX(pPos.getX());
    			setY(pPos.getY());
    			setZ(pPos.getZ());
    		}
    
    		virtual int getX() = 0;
    		virtual int getY() = 0;
    		virtual int getZ() = 0;
    
    		virtual void setX(int pX) = 0;
    		virtual void setY(int pY) = 0;
    		virtual void setZ(int pZ) = 0;
    
    };
    

    Fehlermeldung: Unzulässiger Kopierkonstruktor: erster Parameter darf nicht 'Position3D' sein
    Jemand eine Idee wie ich das machen kann?



  • Den Kopierkonstrukter implementiert man normalerwiese über ein const T& also:

    Position3D(const Position3D& pPos) {
                setX(pPos.getX());
                setY(pPos.getY());
                setZ(pPos.getZ());
            }
    

    Und nebenbei bemerkt ist genau das ein Fall, in dem get/set besonders hässlich ist.



  • Hi!

    Ja die Idee hatte ich auch, aber dabei meckert Visual-C++ immer rum:
    "error C2662: 'Position3D::getX': this-Zeiger kann nicht von 'const Position3D' in 'Position3D &' konvertiert werden. Durch die Konvertierung gehen Qualifizierer verloren"

    In diesem Fall sind get/set Methoden jedoch einaml wirklich unerlässlich, denn:
    Normalerweise speichert man die Position mit normalen int's (klare Sache).Aber da ich sehr viele (2000*2000*1000) Punkte und ein paar weitere Informationen pro Punkt speichern muss, speichere ich die einzelnen Koordinaten in einem 11 bzw. 10 Bit-Feld, wobei die get/set-Methoden hier dann entsprechende Umrechnungen vornehmen müssen.



  • Blub21648215 schrieb:

    ...

    Ich sehe gleich mehrere Probleme...

    1. Wie schon von 314... genannt sollte die Übergabeparameter "const &" sein. Hintergrund ist ganz einfach, in deiner Version wird der Parameter bereits kopiert (Copy by Value), wodurch wiederum ein Kopierkonstruktor nötig wäre. Du solltest dich mit den Konventionen zur Parameterübergabe beschäftigen (Copy by Value...).

    2. Selbst wenn dein Kopierkonstruktor soweit abgeändert ist, gibt es das Problem das die Basisklasse immer zuerst konstruiert wird. Der Aufruf von virtuellen Methoden direkt oder indirekt aus einem Konstruktor kann zu undefinierten/ungewünschten Verhalten verhalten führen, da die Daten der abgeleiteten Klasse erst danach konstruiert werden.

    Stell dir einfach vor du hast eine Basisklasse A und eine davon abgeleitete Klasse B, wenn du nun ein B Objekt generierst (unabhängig davon ob über einen normalen, oder den Kopierkonstruktor), wird immer erst der A-Anteil generiert, der Aufruf einer virtuellen Funktion wird daher die Funktion wählen die im A-Anteil definiert wurde, nicht im B-Anteil.

    Ich sehe in deinen Fall ohnehin keinen Sinn darin, Daten sollte man immer in der Klasse initialisieren, in der sie deklariert werden. Wenn x,y,z nicht in der Basisklasse deklariert werden, gehören sie auch nicht darin initialisiert. Im Kopierkonstruktor der entsprechenden, konkreten Klasse ist dies wiederum unproblematisch.



  • Blub21648215 schrieb:

    Ja die Idee hatte ich auch, aber dabei meckert Visual-C++ immer rum:
    "error C2662: 'Position3D::getX': this-Zeiger kann nicht von 'const Position3D' in 'Position3D &' konvertiert werden. Durch die Konvertierung gehen Qualifizierer verloren"

    Getter sollten das Objekt nicht ändern, daher als Konstant markiert werden.

    virtual int getX() const = 0;
    

    Blub21648215 schrieb:

    In diesem Fall sind get/set Methoden jedoch einaml wirklich unerlässlich, denn:

    Aber für den Kopierkonstruktor dennoch nicht geeignet.



  • Das heißt konkret:
    Ich soll den Copyconstructor in jeder Oberklasse (die nicht mehr abstrakt ist) definieren und implementieren?
    Was ist dann aber mit Methoden, die den Copyconstructor brauchen, aber auf Position3D arbeiten? Falls ich den copyc'tor für eine Klasse nicht definieren, bekomme ich dann erst zur Laufzeit einen Fehler (da copyc'tor für diese Klasse nicht gefunden wird) oder meckert der compiler vorher schon rum, dass der copyc'tor gar nicht vorhanden ist?
    Mal etwas konkreter:

    class someClass {
      //...
      Position3D pos;
      someClass(const Position3D &pos);
      //...
    

    Ich möchte nun in someClass die Position speichern und mit dem c'tor schon initialisieren. Welche Position genau ich verwende, ist mir hier aber egal.



  • Blub21648215 schrieb:

    class someClass {
      //...
      Position3D pos;
      someClass(const Position3D &pos);
      //...
    

    Das funktioniert so nicht. Position3D ist eine abstrakte klasse, damit kannst du keine Membervariablen von dem typ haben.

    Ich möchte nun in someClass die Position speichern und mit dem c'tor schon initialisieren. Welche Position genau ich verwende, ist mir hier aber egal.

    Was du vielleicht suchst, ist ein pointer auf Position3D als Member für someClass und eine clone-Methode für Position3D:

    class someClass {
      //...
      Position3D* pos; //pointer!
      someClass(const Position3D &pos_)
        : pos(pos_.clone())
      {}
      //...
    


  • Blub21648215 schrieb:

    Das heißt konkret:
    Ich soll den Copyconstructor in jeder Oberklasse (die nicht mehr abstrakt ist) definieren und implementieren?

    Ja.

    Blub21648215 schrieb:

    Was ist dann aber mit Methoden, die den Copyconstructor brauchen, aber auf Position3D arbeiten?

    Das ist eh unmöglich, da man nicht direkt auf Position3D arbeiten kann (Da die Klasse abstrakt ist). Ich nehme mal an du kommst von Java?

    Dein folgendes Beispiel kann unter C++ garnicht funktionieren:

    Blub21648215 schrieb:

    class someClass {
      //...
      Position3D pos;
      someClass(const Position3D &pos);
      //...
    

    Du kannst nicht von einer abstrakten Basisklasse ein Objekt halten, und selbst wenn Position3D nicht abstrakt wäre, würde es nicht das tun was du erwartest (Such mal nach "Slicing").



  • Hm es dämmert mir wo der Fehler liegt. In der Tat, OOP programmiere ich hauptsächlich mit Java, da ich aber auch einiges im Mikrocontroller-Bereich mache, kenne ich mich halt auch mit C/C++ aus (nur eben nicht was OOP angeht :D)

    In Java ist es möglich eine abstrakte Klasse zu definieren, die bestimmte Eigenschaften haben soll (geht ja auch in C++). Nun kann ich in Java jedoch auch mit der abstrakten Klasse arbeiten, wie mit einer konkreten,wenn ich mich nur für die Methoden interessiere, die die abstrakte Klasse bereitstellt. Dazu muss ich nur irgendwann die Variable (vom Typ der abstrakten Klasse), auf ein konkretes Objekt (einer Oberklasse der abstrakten Klasse) "zeigen" lassen. Und schon stehen mir alle Funktionen zur Verfügung, die in der konkreten Klasse implementiert worden sind und in der abstrakten Klasse definiert wurden.
    Genau das wollte ich hier auch machen, aber anscheinend geht genau das nicht?!

    @pumuckl: Wie soll ich denn diese clone Funktion implementieren? Wenn pos vom Typ Position3D ist und einen Zeiger auf ein Objekt vom Typ Position3D zurückgibt, dann muss ich doch irgendwo in clone() auch mal "...new..." ausführen. Wie soll ich dass denn machen, wenn die abstrakte Oberklasse gar nicht weiß, welche konkrete Klasse genommen werden soll?



  • Blub21648215 schrieb:

    Nun kann ich in Java jedoch auch mit der abstrakten Klasse arbeiten, wie mit einer konkreten,wenn ich mich nur für die Methoden interessiere, die die abstrakte Klasse bereitstellt.

    "Mit einer abstrakten Klasse arbeiten" ist sehr, sehr ungenau, und genau darin liegt der Hund begraben, bzw. der in dieser Hinsicht extrem wichtige Unterschied zwischen C++ und Java:

    1. Du arbeitest grundsätzlich nicht mit Klassen, sondern mit Objekten, d.h. mit Instanzen dieser Klassen. Klassen sind etwas abstraktes.
    2. In Java hantierst du nicht mit den Objekten selber, sondern mit Referenzen darauf. Diese Referenzen können auch auf abgeleitete Klassen verweisen. In C++ benutzt man dafür Zeiger (oder auch C++-Referenzen)

    Beispiel Java:

    MyClass m; // m ist eine Referenz auf ein Objaket vom Typ MyClass oder eines abgeleiteten Typs oder null.
    

    Beispiel C++:

    MyClass m; // m ist ein Objekt vom Typ MyClass. Punkt.
    MyClass* pm; //pm ist ein Zeiger auf ein Objekt vom Typ MyClass oder von einem abgeleiteten Typ oder NULL
    

    Das ist einer der großen Unterschiede zwischen Java und C++, und an diesem Unterschied hängen auch andere Unterschiede, z.B. was Gleichheit angeht und was das Erzeugen von Objekten angeht.
    Fazit: Lerne C++ nicht, indem du versuchst, die Parallelen zu Jave auf eine andere Sprache zu übertragen. Lerne es, indem du erstmal alles vergisst, was du von Java her weißt. Wenn du auf etwas stößt, was ähnlich sein könnte vergewissere dich, ob es das auch ist (häufig ist es das nämlich nicht).


Anmelden zum Antworten