Probleme mit virtueller Vererbung



  • Hallöchen,

    ich habe ein recht spezielles Problem. Ich habe eine Klassenhierarchie, in der verschiedene Typen von Pfad-Segmenten (aus SVG, z.B. MoveTo, LineTo, CubicCurve, ...) von einer Basisklasse erben. Diese hat die Methode calcAbsBounds(..), um die Boundingbox zu berechnen. Dafür muss diese natürlich von den abgeleiteten Klassen überschrieben werden.

    Leider passiert es, wenn ich einen Vektor mit Pfadsegmenten habe und ich nacheinander alle Elemente durchlaufe, dass nicht die überschriebene Funktion aufgerufen wird, sondern die der Basisklasse. Warum nur??

    Ich habe mal die ganzen Sachen versucht, zusammen zu kürzen:

    -------------------------------------------
    Basisklasse:
    -------------------------------------------
    class CtSVGPathSeg
    {
    public:
       ...
       virtual CtSVGPoint calcAbsBounds(const CtSVGPoint& start, float &x, float &y, float &w, float &h) const;
    };
    
    -------------------------------------------
    Marker-Interface:
    -------------------------------------------
    class CtSVGPathSegMoveInterface : public virtual CtSVGPathSeg
    {
    };
    
    -------------------------------------------
    abgeleitete Klasse:
    -------------------------------------------
    class CtSVGPathSegMovetoRel : public virtual CtSVGPathSegMoveInterface
    {
    public:
       ...
       virtual CtSVGPoint calcAbsBounds(const CtSVGPoint& start, float &x, float &y, float &w, float &h) const;
    private:
       float _x;
       float _y;
    };
    
    -------------------------------------------
    aufrufender Code:
    -------------------------------------------
       ...
    1   CtSVGPathSegList segList(getAttribute("d"));
    2
    3   unsigned long nItems = segList.getNumberOfItems();
    4   CtSVGPoint lastPos;
    5
    6   for(unsigned long i = 0; i < nItems; i++)
    7      lastPos = (segList.getItem(i)).calcAbsBounds(lastPos, x, y, w, h);
       ...
    

    Puh!

    Naja, in Zeile 7 des Letzen Codes wird leider nicht die Funktion des korrekten Segments aufgerufen, sondern die der Basisklasse. Die Liste gibt eine Refernz auf ein Objekt der Basisklasse zurück, welches aber eigentlich ein Objekt einer abgeleiteten Klasse ist.

    Kann jemand mein Problem verstehen und mir helfen??? Das wäre genial.



  • Um das Problem zu verstehen wäre es noch hilfreich wenn du auch die Klasse CtSVGPathSegList gepostet hättest. Wenn diese aber einfach nur eine Liste mit CtSVGPathSeg-Klassen verwaltet, und so wie das aus dem Code aussieht CtSVGPathSegList::getitem ein normales Objekt vom Typ CtSVGPathSeg zurückliefert, dann wird sinnigerweise natürlich auch CtSVGPathSeg::calcAbsBounds aufgerufen. Was anderes wäre es natürlich wenn ein Zeiger zurück geliefert werden würde, und dieser unter Umständen eben auf ein OBjekt vom typ CtSVGPathSegMovetoRel zeigt, dann würde die Polymorphie ansprechen und die gewollte Funktion aufgerufen. So wie ich das aber jetzt sehe, liefert GetItem ein reines Objekt vom Typ CtSVGPathSeg zurück.



  • Ok, das sehe ich ein. Hast recht.

    class CtSVGPathSegList
    {
    public:
       const CtSVGPathSeg& getItem(const unsigned long index) const;
    
    private:
    	std::vector<CtSVGPathSeg> _vec;
    };
    

    So, wie bereits kurz erwähnt, wird eine Referenz zurück gegeben. Ich möchte ja die Polymorphie nutzen, aber es geht halt irgendetwas schief.



  • FelixManke schrieb:

    std::vector<CtSVGPathSeg> _vec;
    

    ändern in

    std::vector<CtSVGPathSeg*> _vec;
    

    und alle Methoden von CtSVGPathSegList anpassen. Durch Verwenden von Zeigern wird die Polymorphie möglich.



  • Hopla, nagut, dann weiß ich, woran ich bin.

    Kann ich denn auch nur beim Aufruf der Methode einen Pointer verwenden und so an die korrekte Methode kommen? Das würde WIRKLICH viel Arbeit sparen!!!!



  • FelixManke schrieb:

    Hopla, nagut, dann weiß ich, woran ich bin.

    Kann ich denn auch nur beim Aufruf der Methode einen Pointer verwenden und so an die korrekte Methode kommen? Das würde WIRKLICH viel Arbeit sparen!!!!

    Du meinst, dass CtSVGPathSegList::getItem einen Zeiger zurückliefert?
    Falls ja, denke ich ist das keine Möglichkeit, da du dann immer einen Zeiger auf ein CtSVGPathSeg-Objekt zurückgibst. Der Sinn aber ist, dass du in deinem Vektor mehrere CtSVGPathSeg-Zeiger speicherst, die jeweils auf ein passendes Objekt vom Typ CtSVGPathSeg oder CtSVGPathSegMovetoRel oder sonstigen Kinderklassen zeigen, und bei Aufruf der Methode calcAbsBounds jeweils die richtige ausgewählt wird.

    Also kurz und bündig: Du musst dein Vektor neu definieren, und damit wohl auch sonst deine Klasse etwas umschreiben.

    Grüße,
    Fluxx



  • FelixManke schrieb:

    Kann ich denn auch nur beim Aufruf der Methode einen Pointer verwenden und so an die korrekte Methode kommen? Das würde WIRKLICH viel Arbeit sparen!!!!

    Du musst nur die Klasse CtSVGPathSegList ändern, den Rest kannst du lassen:

    class CtSVGPathSegList
    {
    public:
       const CtSVGPathSeg& getItem(const unsigned long index) const {
          return *vec[index];
       }
    
    private:
    	std::vector<CtSVGPathSeg*> _vec;
    };
    


  • Hi,

    ich habe jetzt alles auf Pointer umgestellt. Dann habe ich es einheitlich und muss nicht immer auf Pointer / Referenzen aufpassen.

    Aber vielen Dank, an alle, die mir geantwortet haben. Ich wusste nicht, dass Polymorphie nur mit Pointern funktioniert. Jetzt aber schon.


Anmelden zum Antworten