Xlander
Xlander

Reputation: 1331

Getting derived class from base method

I have 3 classes Entity, Character and Item. When I do

Character * Player = new Character(QRectF(0, 0, 50, 50), "player", theScene);
Player->setVelocityX(30)->setAttackPoint(10);

The compiler tells that error: 'class Entity' has no member named 'setAttackPoint'. How can I make Entity* setVelocityX(qreal vx); return a Character pointer or an Item pointer?

.

class Entity : public QObject, public QGraphicsPolygonItem
{
    Q_OBJECT

    public:
        Entity();
        Entity(qreal x, qreal y, qreal w, qreal h, QString tag, QGraphicsScene *scene = 0, QGraphicsItem *parent = 0);
        Entity(QRectF position, QString tag, QGraphicsScene *scene = 0, QGraphicsItem *parent = 0);
        Entity(QString tag, QGraphicsScene *scene = 0, QGraphicsItem *parent = 0);
        Entity* setVelocityX(qreal vx);
        // etc
}


class Character : public Entity
{
    Q_OBJECT

    public:
        Character(qreal x, qreal y, qreal w, qreal h, QString tag, QGraphicsScene *scene = 0, QGraphicsItem *parent = 0);
        Character(QRectF position, QString tag, QGraphicsScene *scene = 0, QGraphicsItem *parent = 0);
        Character(QString tag, QGraphicsScene *scene = 0, QGraphicsItem *parent = 0);
        Character* setAttackPoint(int attackPoint);
        //etc
}


class Item : public Entity
{
    Q_OBJECT

    public:
        enum ItemType{
            Consummable,
            Special,
            Weapon
        };

        Item(QString name, qreal x, qreal y, qreal w, qreal h,  QString tag = "item", QGraphicsScene *scene = 0, Character *parent = 0);
        Item* setOwner(Character* newOwner);
        //etc
}

Upvotes: 0

Views: 107

Answers (3)

Andy Reimann
Andy Reimann

Reputation: 578

There is also another way of achieving what you want to do: Curriously Recurring Template Pattern

The basic idea is that the base class has a template parameter which is the derived class. Then you implement a non virtual function in every derived class which can then be called using a static_cast like that (simplified class layout):

Entity.h:

#include <iostream>

template<class DERIVED_CLASS>
class Entity
{
public:
    void doStuff()
    {
        static_cast<DERIVED_CLASS*>(this)->doStuffDerived();
    }
};


class Character : public Entity<Character>
{
public:
    void doStuffDerived()
    {
        // implementation
        std::cout << "character\n";
    }
};


class Item : public Entity<Item>
{
public:

    void doStuffDerived()
    {
        // implementation
        std::cout << "item\n";
    }
};

main.cpp:

#include "Entity.h"

int main()
{
    // Instantiate a n Item object
    Entity<Item> item;
    item.doStuff();
    // Instantiate a character object 
    Character character;
    character.doStuff();
}

With this method you actually do not have the cost of a virtual function call but can implement a different behavior for every implementation which is kinda like a virtual function (a good compiler can easily optimize this code to only a function call on the derived class).

And you also do not have to rely on a reinterpret_cast, which should also be prevented if possible.

Here are some more informations: Curiously recurring template pattern

Upvotes: 1

Luca Davanzo
Luca Davanzo

Reputation: 21520

Character* player = new Character(QRectF(0, 0, 50, 50), "player", theScene);
dynamic_cast<Character*>(player->setVelocityX(30))->setAttackPoint(10);

By the way this method is not type safe. You need to add some controls.

Generally, SETS methods are used to set some value, not to return an object.

Character* player = new Character(QRectF(0, 0, 50, 50), "player", theScene);
Entity* playerEntity = player->setVelocityX(30);
Character* isCharacter = dynamic_cast<Character*>(playerEntity);
if(isCharacter) {
   isCharacter->setAttackPoint(10);
}

Upvotes: 1

George Houpis
George Houpis

Reputation: 1729

If you are absolutely certain on the type (otherwise use dynamic_cast and check the resulting pointer for non-nullptr):

reinterpret_cast< Character * >( Player->setVelocityX(30))->setAttackPoint(10)

Also, based on your code, couldn't you go instead or is the order of the function calls significant?

Player->setAttackPoint(10)->setVelocityX(30)

Upvotes: 2

Related Questions