Klassen Vererbung Beispiel



  • Hallo,
    bin grade bei einer Übungsaufgabe in meinen Buch und wollte mal wissen was ihr besser machen würdet.
    Ich war ziemlich unsicher bei "GeometricObjects" und hätte da ein paar Fragen.
    -Bei den Member "position" arbeite ich mit einen pair bei "move" aber nicht. Der Gedanke dahinter war das man ja nicht mit einen Punkt bewegt sondern mit zwei Werten
    -Würde man für "std::pair<int,int>" ein Typedef benutzen?
    Würde man den dann so gestalten "using pos = std::pair<int,int>;" ?
    Wo würde man diesen Platzieren? Zwischen Zeile 5 und 6?
    -EDIT: Den Konstruktor kann man in public packen oder? Durch den Destruktor verhinder ich ja schon das erstellen von Objekten.

    Ist das Klassendesign so richtig?
    Stört euch was an der Formatierung?

    Die Aufgabe:

    C++ Primer 5th schrieb:

    Exercise 15.21: Choose one of the following general abstractions containing
    a family of types (or choose one of your own). Organize the types into an
    inheritance hierarchy:
    (a) Graphical file formats (such as gif, tiff, jpeg, bmp)
    (b) Geometric primitives (such as box, circle, sphere, cone)
    (c) C++ language types (such as class, function, member function)
    Exercise 15.22: For the class you chose in the previous exercise, identify
    some of the likely virtual functions as well as public and protected
    members.

    Code:

    #include <string>
    #include <utility>
    
    class GeometricObjects
    {
    public:
    	virtual ~GeometricObjects() = 0;
    
    	const std::string name() const {return m_Name;}
    	std::pair<int,int> position() const{return std::pair<int, int>(m_centralPosX, m_centralPosY);}
    
    	void move(int x, int y){m_centralPosX += x; m_centralPosY += y;}
    protected:
    	GeometricObjects(std::string name = "noName", int centralPosX = 0, int centralPosY = 0) :
    		m_Name(name), m_centralPosX(centralPosX), m_centralPosY(centralPosY){}
    private:
    	const 	std::string m_Name;
    	int		m_centralPosX;
    	int 	m_centralPosY;
    };
    
    GeometricObjects::~GeometricObjects(){}
    
    class Square : public GeometricObjects
    {
    public:
    	Square(std::string name = "noName", int centralPosX = 0, int centralPosY = 0, 
    		unsigned int size = 1) :
    			GeometricObjects(name, centralPosX, centralPosY),
    			m_size(size) {}
    
    	void resize(unsigned int size){m_size = size;}
    private:
    	unsigned int m_size;
    };
    
    class Rectangle : public GeometricObjects
    {
    public:
    	Rectangle(std::string name = "noName", int centralPosX = 0, int centralPosY = 0,
    		unsigned int sizeX = 1, unsigned int sizeY = 1) :
    			GeometricObjects(name, centralPosX, centralPosY),
    			m_sizeX(sizeX), m_sizeY(sizeY) {} 
    
    	void resize(unsigned int sizeX, unsigned int sizeY){m_sizeX = sizeX; m_sizeY = sizeY;}
    private:
    	unsigned int m_sizeX, m_sizeY;
    };
    

    Würde mich freuen wenn ihr mir helfen könntet.



  • Du hast in deinem Beispiel keine einzige virtuelle Funktion drinnen. Ich denke nicht dass dein Prof/Lehrer damit zufrieden sein wird.

    Es geht wohl eher um etwas, wo man über eine Basisklasse bestimmte Operationen auf Objekten durchführen kann, unabhängig davon was für eine konkrete Klasse das Objekt hat, wobei die Operationen dann in den verschiedenen konkreten Klassen unterschiedliche implementiert sind.

    Beispielsweise wäre eine "Print" Funktion für alle geometrichen Formen implementierbar, müsste aber für jede konkrete Klasse anders implementiert werden. Oder Funktionen wie "get area", "get bounding box", "scale" etc.



  • Ok, hab jetzt eine Print hinzugefügt. Auf scale habe ich jetzt einfach mal verzichtet ist ja genau das gleich bloß das man sich das überladen des operators sparen kann.

    Was meint ihr was geht besser?

    #include <string>
    #include <utility>
    #include <iostream>
    
    class GeometricObjects
    {
    public:
    	GeometricObjects(std::string name = "noName", int centralPosX = 0, int centralPosY = 0) :
    		m_name(name), m_centralPosX(centralPosX), m_centralPosY(centralPosY){}
    	virtual ~GeometricObjects() = 0;
    
    	virtual void print(std::ostream& os) const
    		{os << "Name: " << m_name << " X: " << m_centralPosX << " Y: " << m_centralPosY;}
    	const std::string name() const {return m_name;}
    	std::pair<int,int> position() const{return std::pair<int, int>(m_centralPosX, m_centralPosY);}
    
    	void move(int x, int y){m_centralPosX += x; m_centralPosY += y;}
    private:
    	const 	std::string m_name;
    	int		m_centralPosX;
    	int 	m_centralPosY;
    };
    
    GeometricObjects::~GeometricObjects(){}
    
    class Square : public GeometricObjects
    {
    public:
    
    	Square(std::string name = "noName", int centralPosX = 0, int centralPosY = 0, 
    		unsigned int size = 1) :
    			GeometricObjects(name, centralPosX, centralPosY),
    			m_size(size) {}
    
    	void print(std::ostream& os) const override
    		{GeometricObjects::print(os); os << " Size: " << m_size;}
    
    	void resize(unsigned int size){m_size = size;}
    private:
    	unsigned int m_size;
    };
    
    class Rectangle : public GeometricObjects
    {
    public:
    	Rectangle(std::string name = "noName", int centralPosX = 0, int centralPosY = 0,
    		unsigned int sizeX = 1, unsigned int sizeY = 1) :
    			GeometricObjects(name, centralPosX, centralPosY),
    			m_sizeX(sizeX), m_sizeY(sizeY) {} 
    
    	void print(std::ostream& os) const override
    		{GeometricObjects::print(os); os << " SizeX: " << m_sizeX << " SizeY: " << m_sizeY;}
    
    	void resize(unsigned int sizeX, unsigned int sizeY){m_sizeX = sizeX; m_sizeY = sizeY;}
    private:
    	unsigned int m_sizeX, m_sizeY;
    };
    
    std::ostream& operator<<(std::ostream& os, const GeometricObjects& obj)
    {
    	obj.print(os);
    	return os;
    }
    
    #include <iostream>
    
    int main()
    {
    	Rectangle s1;
    	std::cout << s1;
    
    }
    


  • Ich meinte mit "scale" eher sowas in der Basisklasse:

    // Basisklasse:
    virtual void scale(double factor) = 0;
    
    // Quadrat:
    virtual void scale(double factor)
    {
        m_size *= factor;
    }
    
    // Rechteck:
    virtual void scale(double factor)
    {
        m_sizeX *= factor;
        m_sizeY *= factor;
    }
    

    Ich denke allerdings dass die File-Format Geschichte viel besser geeignet ist um zu zeigen wie man sinnvoll mit Vererbung und virtuellen Funktionen arbeiten kann.
    Ich meine sogar dass File-Formate das Bilderbuch Beispiel schlechthin sind - das passt so wunderbar, schöner geht's kaum.



  • CraftPlorer schrieb:

    -Bei den Member "position" arbeite ich mit einen pair bei "move" aber nicht. Der Gedanke dahinter war das man ja nicht mit einen Punkt bewegt sondern mit zwei Werten

    Der Punkt besteht auch aus zwei Werten. Trotzdem mag man den Punkt(x|y) gerne zu einem Objekt zusammenfassen. Wenn man eine Verschiebung(x|y) auch zu einem Objekt zusammenfasst, nennt man das https://oberprima.com/mathematik/vektorrechnung/ , und die ist saupraktisch.

    -Würde man für "std::pair<int,int>" ein Typedef benutzen?

    NEIN! std::pair hat im Code nur extrem selten was zu suchen!
    Sonst sieht der Code aus wie in diesem Beispiel: http://www.c-plusplus.net/forum/326688-full
    Das mag keiner nach zwei Wochen debuggen.

    CraftPlorer schrieb:

    -EDIT: Den Konstruktor kann man in public packen oder? Durch den Destruktor verhinder ich ja schon das erstellen von Objekten.

    Ähm...
    Wozu das Erstellen verhindern? Hab sowas lange nicht mehr gebraucht. Bei mir ergibt sich immer, daß die Basisklasse auch irgendwo eine sinnvolle absstrakte Methode hat. Jo, die print zum Bleistift.

    CraftPlorer schrieb:

    Ist das Klassendesign so richtig?

    Es ist nicht falsch, aber zeigt auch nicht so richgtig dolle, was Polymorphie ist. In der main() ein vector<GeometricObject*> welt; würde was reißen.



  • hustbaer schrieb:

    Ich meinte mit "scale" eher sowas in der Basisklasse:

    Ich weiß aber ich war zu faul da ich die Aufgabe schnell fertig haben wollte deswegen hab ich die weggelassen.

    hustbaer schrieb:

    Ich denke allerdings dass die File-Format Geschichte viel besser geeignet ist um zu zeigen wie man sinnvoll mit Vererbung und virtuellen Funktionen arbeiten kann.
    Ich meine sogar dass File-Formate das Bilderbuch Beispiel schlechthin sind - das passt so wunderbar, schöner geht's kaum.

    Ich hatte keine Lust alle Formate nach zu schauen da ich noch nie damit gearbeitet habe.

    volkard schrieb:

    Der Punkt besteht auch aus zwei Werten. Trotzdem mag man den Punkt(x|y) gerne zu einem Objekt zusammenfassen. Wenn man eine Verschiebung(x|y) auch zu einem Objekt zusammenfasst, nennt man das https://oberprima.com/mathematik/vektorrechnung/ , und die ist saupraktisch.

    Ok hatte Vektorberechnungen leider nie in der Schule. 😞

    volkard schrieb:

    NEIN! std::pair hat im Code nur extrem selten was zu suchen!

    Wann würde man es denn zum Beispiel benutzen?

    volkard schrieb:

    Ähm...
    Wozu das Erstellen verhindern? Hab sowas lange nicht mehr gebraucht. Bei mir ergibt sich immer, daß die Basisklasse auch irgendwo eine sinnvolle absstrakte Methode hat.

    Alles klar hatte es bloß gebraucht wo ich noch kein print hatte.

    volkard schrieb:

    Jo, die print zum Bleistift.

    Hat das eine tiefere Bedeutung die ich nicht verstehe?

    volkard schrieb:

    Es ist nicht falsch, aber zeigt auch nicht so richgtig dolle, was Polymorphie ist. In der main() ein vector<GeometricObject*> welt; würde was reißen.

    Ich wollte in der main eigentlich gar nichts schreiben war ja nicht die Aufgabe


Anmelden zum Antworten