user2946316
user2946316

Reputation:

c++ - Inheritance and cast

I'm writing a 2d game and want to make it right from the beginning. I'm not entirely sure how to solve multiple inheritance and casting.

Lets say for instance that i have a game world. I create a class called Environment which all the objects will inherit.

Now if i create another class called Brick and Ground, this two classes are going to inherit the functions from environment. Now lets say i want to be able to explode the Brick upon contact, but the ground class won't ever use it, so it would be a waste to create a function like that in the environment class and make it virtual.

So i though, maybe if i cast the environment object back to the derived class and call its functions from there: (Only a example below to demonstrate the though process)

#include <iostream>
#include <string>
#include <vector>
#include <limits>

class Environment
{
protected:
    std::string name;
    int health;
    int damage;
public:
    Environment(std::string _name)
    {
        this->name = _name;
    }

    virtual std::string GetType() = 0;

    std::string GetName()
    {
        return this->name;
    }

    void Spawn()
    {
        std::cout << this->name << ": Spawning object..." << std::endl;
    }

    int GetHealth()
    {
        return this->health;
    }

    void TakeDamage(int _damage)
    {
        this->health-=_damage;
    }

    virtual void Update() = 0;
    virtual void Render() = 0;
    virtual ~Environment(){}
};

class Ground : public Environment
{
private:

public:
    Ground(std::string _name) : Environment(_name)
    {

    }

    std::string GetType()
    {
        return "Ground";
    }

    void Update()
    {
        std::cout << "Updating: " << this->name << std::endl;
    }

    void Render()
    {

    }

};

class Brick : public Environment
{
private:

public:
    Brick(std::string _name) : Environment(_name)
    {
        this->name = "Brick Ansikte";
        this->health = 100;
    }

    std::string GetType()
    {
        return "Brick";
    }

    void Update()
    {
        std::cout << "Updating: " << this->name << std::endl;
    }

    void Render()
    {

    }

    void Explode()
    {
        std::cout << "Exploding BRICK" << std::endl;
    }
};

int main()
{
    std::vector<Environment*> listEnvironment;
    listEnvironment.push_back(new Ground("Ground fool"));
    listEnvironment.push_back(new Brick("Ground tun"));

    for(auto& env : listEnvironment)
    {
        std::cout << "HP: " << env->GetHealth() << std::endl; 
        env->Spawn();
        env->Update();
        if (env->GetType() == "Brick")
        {
            std::cout << "BRICK TYPE WAS FOUND, DYNAMIC CAST" << std::endl;
            static_cast<Brick*>(env)->Explode();

        }

        delete env;
    }
    listEnvironment.empty();


    std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
    return 0;
}

Is this solution good or bad? Or should i create yet another base helper class that holds all the different type of actions that can occur in the game? Like explode, bounce etc etc?

Best regards nilo

Upvotes: 0

Views: 116

Answers (1)

kirbyfan64sos
kirbyfan64sos

Reputation: 10727

There are two solutions:

  1. Use dynamic_cast:

    if (Brick* brick = dynamic_cast<Brick*>(env))
    {
        std::cout << "BRICK TYPE WAS FOUND, DYNAMIC CAST" << std::endl;
        brick->explode();
    }
    
  2. Add another method to Environment named something like MaybeExplode (that's actually a really bad name, but I can't really think of something better):

    virtual void MaybeExplode() {}
    

    Now, override it in Brick:

    void MaybeExplode()
    {
        std::cout << "Exploding BRICK" << std::endl;
    }
    

    Then unconditionally call MaybeExplode in your for loop:

    ...
    env->Spawn();
    env->Update();
    env->MaybeExplode();
    
    delete env;
    

    This is not wasteful. It's really how OO design is. The person asking for an explosion doesn't know what env is.

    Also, dynamic_cast is actually slower than a virtual function, so, if anything, your current version is more wasteful.

Upvotes: 2

Related Questions