Premiär! Fyndchans i SweClockers Månadens Drop

C++ Initialiseringslista med arv från abstrakt klass

Permalänk
Medlem

C++ Initialiseringslista med arv från abstrakt klass

Tjena!

jag håller på att hacka ihop ett litet side scroller-spel baserat på C++ med Qt. Tänk 2D, rymdskepp som kan skjuta och undvika hinder.

De flesta klasser i spelet ärver av en otroligt generell abstrakt klass, "GameObject". Där finns generella medlemsfunktioner så som "moveRight()", "update()" osv osv. GameObject har också ett privat attribut "m_body", som representes av en QRect. Detta attribut används för att representera kanten runt ett objekt i spelet, till exempel spelarens rymdskepp, ett "skott" eller en asteroid. Spelarens skepp (kort och gott klassen "Ship") ärver direkt av "Gameobject". För att initiera "Ship" och tilldela värden till det privata attributet m_body i superklassen GameObject skriver jag följande i Ship:s konstruktör:

ship::ship() : gameObject(100, 350, SHIP_WIDTH, SHIP_HEIGHT)

där samtliga argument blir tilldelade till m_body.
Men! När jag vill skapa min första fiende, Enemy_one, hade det varit otroligt smidigt att ärva av "Ship" hellre än "GameObject", eftersom ett fiendeskepp kommer ha mycket gemensamt med spelaren skepp. Jag kan dock inte initiera denna på samma sett, eftersom "Ship" inte har ett privat attribut "m_body", det ligger ju i superklassen.

Nu tänker ni säkert att det bara är att skriva om det till att konstruktören i Ship blir sån här:

ship::ship(int xPos, int yPos, int width, int heigh) : GameObject(t100, 350, SHIP_WIDTH, SHIP_HEIGHT)

Och ändra till motsvarande i Enemy_one. Men då måste jag tydligen ha en default-konstruktör, och då är det alltid denna default-konstruktör som anropas av någon anledning.

TL;DR: Hur tilldelar jag attributet m_body i Gameobject sina värden i Enemy_one, som ärver av Ship som i sin tur ärver av GameObject?

Off topic: Finns det några kod-formaterings-taggar?

Permalänk
Medlem

Jag har en fråga bara.

Varför vill du ha ett sådant långt arv?
Känner ju att Enemy kan ha ett ship istället för att vara ett ship. Förutsatt att ship inte är abstrakt den med.

Men om du behöver ha tillgång till en variabel längre ner i arvsträdet kan du väl sätta den till protected istället för private?

Permalänk
Medlem
Skrivet av jH0Ni:

Men då måste jag tydligen ha en default-konstruktör, och då är det alltid denna default-konstruktör som anropas av någon anledning.

Det är nog detta du behöver klura ut, för default-konstruktorn anropas inte utan anledning. Lite mer kod skulle vara bra så vi kan se vart det går fel.

Skrivet av jH0Ni:

Off topic: Finns det några kod-formaterings-taggar?

Ja:
[​code]
Kod här
[​/code]

Permalänk
Medlem

hej,

inte säker på att förstått rätt här, men försöker ändå.

du kan ha flera konstruktörer på ship - dvs typ båda de du ger som exempel. sen skapar du enemy som

enemy::enemy(x, y, w, h) : ship(x,y w, h)

och samtidigt ha kvar ship::ship() för att konstruera ett ship direkt.

överkurs: om konstruktören för ship med parametrar bara ska användas av enemy så kan du göra den protected för att få kompileringsfel om man försöker använda den direkt.

cheers

Permalänk
Medlem

Okej, jag bifogar lite kod. Känner att det blev en rörig förklaring, haha. Här är den abstrakta klassen Gamokbject:

class gameObject { public: gameObject(); gameObject(int xPos, int yPos, int width, int height); virtual ~gameObject() {} virtual void update(); virtual void paint(QPainter &painter) = 0; virtual void moveRight() = 0; virtual void moveLeft() = 0; virtual void moveUp() = 0; virtual void moveDown() = 0; virtual bool isAlive() const { return m_isAlive; } virtual QRect getBody() const { return m_body; } protected: QRect m_body; bool m_isAlive; //QPixmap pix; };

Klassen Ship (spelaren själv kan man säga) ärver av Gamobject:

class ship : public gameObject { public: ship(); ~ship(); void paint(QPainter &painter); void moveRight(); void moveLeft(); void moveUp(); void moveDown(); blasterbolt *shoot(); //QRect getShip(); };

För att tilldela tilldela m_body sina värden i Ship ser konstruktören ut såhär:

ship::ship() : gameObject(100, 350, SHIP_WIDTH, SHIP_HEIGHT) { }

Klassen Enemy_one ärver av Ship, alltså ett steg "längre ner" i kedjan kan man säga. Men även Enemy_one behöver ha en "body" (alltså, m_body ifrån GameObject)

class Enemy_one : public ship { public: Enemy_one(); ~Enemy_one(); };

Och implementationen:

Enemy_one::Enemy_one() : ship(300, 250, ENEMY_ONE_WIDTH, ENEMY_ONE_HEIGHT) //Här blir det ju fel såklart { } Enemy_one::~Enemy_one() { }

Permalänk
Medlem

Om jag hade varit du så hade jag nog sett till att Enemy ärver av GameObject istället för ship.

Om båda dessa har en gemensam implementation av en funktion så kan man ju hoppa virtual och implementera den i GameObject.

Eller är målet att öva på att ärva i mer än ett steg?

Permalänk
Permalänk
Medlem
Skrivet av Manarky:

Om jag hade varit du så hade jag nog sett till att Enemy ärver av GameObject istället för ship.

Om båda dessa har en gemensam implementation av en funktion så kan man ju hoppa virtual och implementera den i GameObject.

Eller är målet att öva på att ärva i mer än ett steg?

Det är väl inget mål som sådant, målet är väl snarare att göra saker på ett effektivt och snyggt sätt. Men okej, det kanske är det bästa.

Permalänk
Medlem

Alright, virtual är ju generellt sätt inte bra för prestanda eftersom det bestäms vilken funktion som skall köras under runtime. Men sedan tror jag inte att just denna poängen spelar någon roll i detta fallet.

Kan vara farligt för produktiviteten att optimera småsaker hit och dit som inte spelar så stor roll i det stora hela.

Men alla public funktioner ärvs ju även om dem inte är virtual. Så om dem inte behöver olika implementationer känns det ju lite onödigt.

Permalänk
Medlem
Skrivet av jH0Ni:

ship::ship() : gameObject(100, 350, SHIP_WIDTH, SHIP_HEIGHT) { }

Om jag uppfattat frågan rätt, varför hårdkoda värdena? Borde fungera så som du vill om du gör såhär:

ship::ship(int x, int y, int width, int height) : gameObject(x, y, width, height) { }