Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

1

30.08.2013, 14:37

Vererbung in C++: Abgeleitete Klasse als Basisklasse an Funktion/Methode übergeben

Hallo zusammen,

ich habe da eine Frage bezüglich der Vererbung von Klassen.

Ich habe eine Sprite-Klasse geschrieben, welche unter anderem auf Kollision mit einem anderen Sprite prüfen kann:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
typedef class Sprite 
{ 
public: 
BOOL CheckForCollisionWithSprite(LPSPRITE); 

protected: 
BoundingPolygon, BoundingBox, BoundingCircle etc. 

} Sprite, * LPSPRITE;


Nun ist es so, dass diese Klasse nach den Anforderungen eines Spielobjektes, diesem als Basisklasse dient und entsprechend erweitert wird. So zum Beispiel ein Asteroid:


C-/C++-Quelltext

1
2
3
4
typedef Asteroid : public Sprite 
{ 
… 
} Asteroid, * LPASTEROID;


Jetzt stellt sich natürlich das Problem, dass ich einen solchen Asteroiden nicht einfach an die Funktion der Basisklasse übergeben kann und daher bin ich auf der Suche nach einer möglichst eleganten Methode (Art und Weise, nicht zwangsweise Funktion ;)) dieses Problem zu lösen.

Bisher handhabe ich das stets so, bin aber nicht sicher, ob es nicht auch etwas effizienter geht:


C-/C++-Quelltext

1
2
3
4
5
LPASTEROID lpAsteroid1, lpAsteroid2; 

LPSPRITE ptr = static_cast <LPSPRITE> (lpAsteroid2); 

lpAsteroid1-> CheckForCollisionWithSprite(ptr);


Danke!

2

30.08.2013, 14:57

Meiner Meinung nach gehst du das schon falsch an.
Ein Asteroid IST kein Sprite sondern HAT ein Sprite. Deswegen ist es in meinen Augen schonmal falsch, Asteroid von Sprite erben zu lassen.
Mach Sprite lieber zu einem Member von Asteroid.

Für Polymorphy (so heißt das, was du suchst) musst du mit pointern Arbeiten.

btw. Was sucht eigentlich das typedef vor den klassen? o.O

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

3

30.08.2013, 15:50

Einige Grundfunktionen wie zum Beispiel Texturen zuweisen und anzeigen, Transformieren, Transformationsgrenzen zuweisen und bei der Transformation beachten oder eben Bounding-Daten einzulesen und damit auf Kollision prüfen, all diese Funktionen in einer (Basis-)klasse zu kapseln und davon wiederum konkretere Klassen wie Spielfiguren und andere Elemente abzuleiten erscheint mir nun durchaus sinnvoller als jedes Mal obige Funktionen als eigenständige Member zu implementieren. Das typedef verhilft zur Pointer-Definition am Ende der Klasse.

Dass ich da mit Pointern operieren muss, das habe ich mir gedacht (und auch so gemacht), bin lediglich nicht sicher, ob‘s zu meiner Lösung nicht noch etwas Eleganteres gibt.

4

30.08.2013, 15:58

Jetzt ist halt die Frage, wie definierst du das ganze.
Meiner Ansicht nach, hat ein Bounding Rect innerhalb einer Sprite Klasse nichts zu suchen. Nicht das Sprite kollidiert, sondern eben der Asteroid. Deswegen gehört das auch da rein. Das Sprite ist nur für die Darstellung da.
Zusätzlich kannst du ja auch durchaus von außen das Sprite manipulieren, wenn du der Asteroiden Klasse getter und setter Methoden gibst.
Es ist einfach Fakt, das sowas wie du es da oben vorgestellt hast, auf lange Sicht nicht funktioniert.

Wie gesagt, wundere ich mich über die typedefs, was ist denn Asteroid jetzt genau in deiner Lösung? Letztendlich fehlt mir da ein Schlüsselwort ;)

Du musst auch nicht jedes mal den Pointer down casten, wenn eine Funktion einen Basisklassen Pointer erwartet, das geht direkt (der Compiler erkennt das).

5

30.08.2013, 16:09

Das sieht so aus, als hätte der TS C als Grundlage für C++ gelernt ... ;)

Zitat

Ich bin nicht der Messias.
Ich sage, du bist es, Herr. Und ich muss es wissen, denn ich bin schon einigen gefolgt.

https://bitbucket.org/bwbg

6

30.08.2013, 16:15

Ich würde da auch Anti-Freak sein Weg gehen.

Es hindert dich ja keiner in der Basis klasse die Kollision zu prüfen, eben nur mit einem Sprite als Member.

Gruß Koschi
Wer aufhört besser werden zu wollen hört auf gut zu sein!

aktuelles Projekt:Rickety Racquet

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

7

30.08.2013, 16:32

Na gut, auch wenn‘s vom Thema weggeht, aber wenn es etwas genauer sein muss:

Ich kapsle sämtliche Member und Methoden in einer Basisklasse, mit der Überlegung, welche für wirklich alle Ableitungen sinnvoll wären und bestenfalls auch nicht mehr überschrieben werden müssen (obwohl bei Bedarf natürlich auch mal was überschrieben werden kann).

Und zu diesen Methoden gehören, an meine Bedürfnisse angepasst praktischster Weise die Transformation, Transformationsgrenzen (z.B. der Spielfeld-/Monitorrand), Texturen, Animation usw. Vielleicht irritiert hier lediglich die Bezeichnung Sprite, aber darunter stelle ich mir nun das vor, was da mit einer Textur versehen am Bildschirm angezeigt wird und z.B. bewegt werden kann. Kann eine Spielfigur oder auch ein Hintergrund sein. Letzteres braucht vielleicht nicht unbedingt eine Bounding Box (deswegen gibt es bei mir auch eine gesonderte class AdvancedSprite, welche wiederum von class Sprite abgeleitet ist und erst mit den Bounding Möglichkeiten ausgestattet ist), könnte aber durchaus bewegbar sein müssen.

Was ich nun davon ableite braucht im Normalfall all diese Methoden, hat aber vielleicht zusätzlich noch eine Lebenslinie, kann einen Schuss abfeuern usw.
Mit anderen Worten, ich habe eine Basisklasse die sich in den verschiedensten Projekten sehr einfach wiederverwenden lässt und schon lassen hat.

Wenn ich nun typedef weglasse und am Ende der Klasse, hinter der schließenden Klammer weiterhin z.B. „} Sprite, * LPSPRITE;“ stehen lässt, dann gibt’s (zumindest bei mir) einen Compilerfehler. Deswegen dachte ich mir, ich lass es da stehen ;) , denn eine derart definierte Pointervariable der Klasse zu haben ist nicht selten recht praktisch.

Werde mal versuchen, meinen Pointer ungecastet zu übergeben, obwohl ich mir gerade nicht vorstellen kann, dass mein Compiler das mitmacht.

@ bwbg
C alleine habe ich nie gelernt, kann daher auch nicht sagen, was von meinem Code ein C-Relikt ist und wie man es besser in C++ realisieren kann.

Danke für die Antworten!

8

30.08.2013, 16:49

Ok, also bei Asteroid fehlt definitiv das Schlüsselwort "class". Wenn du nur nen Pointer gesondert benennen willst, dann schreib doch einfach hinter die Klasse:

C-/C++-Quelltext

1
typedef Asteroid* AsteroidPtr;

Oder sowas in der Richtung. Ich persönlich habe die Methode, wie du das Typedef nutzt, noch nie gesehen und war deswegen ein wenig verwundert.

Letztendlich kannst du eine Childklasse immer an einen Basisklassen Pointer übergeben.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo
{
   //...
}

class Bar : public Foo
{
   /...
}

int main()
{
   Foo *pFoo(new Bar());    // geht wunderbar
   Bar *pBar(new Foo());    // geht nicht
}

DaiFei

Frischling

  • »DaiFei« ist der Autor dieses Themas

Beiträge: 28

Wohnort: München

  • Private Nachricht senden

9

30.08.2013, 16:55

Hoppla, da habe ich tatsächlich das "class" übersehen und aufgrund mangelnden Inhalts der Klasse hat das Ganze dann wohl für Verwirrung gesorgt.

Danke für den Tipp, werde das bei nächster Gelegenheit einmal so ausprobieren.

10

30.08.2013, 16:59

Btw:

Quellcode

1
LPASTEROID lpAsteroid1, lpAsteroid2;

Das sind NUR die Pointer, die du hier erstellst. Das ist kein gültiges Object. Versuchst du jetzt per -> operator auf irgendwas zuzugreifen (Methode oder Member) fliegt dir das Ganze um die Ohren.

Quellcode

1
2
LPASTEROID lpAsteroid1(new Asteroid()), lpAsteroid2(new Asteroid());
lpAsteroid1-> CheckForCollisionWithSprite(lpAsteroid2);


Alternativ musst du das gar nicht mal alles auf dem Heap erstellen:

Quellcode

1
2
Asteroid asteroid1, asteroid2;
asteroid1.CheckForCollisionWithSprite(&asteroid2);


PS: Nutzt du eigentlich C++11?

Werbeanzeige