Inheritance problem with code

I used to use Enemy class to make objects, but now i haven't made it abstract yet but i want to. My problem is how to return new instances of objects based on type if they inherits parent? Monster inherits Enemy. How should my getInstanceOfEnemyById() method looks like if I want to return Enemy* to set it for Room if Room enemy field is also Enemy* but enemy in vector is type of Monster? I talk about polymorphism. How to handle it?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//gamedata.cpp
GameData::GameData()
{
    //enemies
    enemies.push_back(new Enemy(1, "Monster1", 1, 5, 10, 25, 50));
    enemies.push_back(new Enemy(2, "Monster2", 2, 10, 15, 50, 100));
    enemies.push_back(new Enemy(3, "Monster3", 3, 15, 20, 66, 100));
    enemies.push_back(new Enemy(4, "Monster4", 3, 15, 20, 66, 100));
    enemies.push_back(new Monster(5, "Monster5", 3, 15, 20, 66, 100));

    //rooms
    Room *room1 = new Room(1, "Room1", "description", new int[4] {2,0,0,0});
    //enemy in room clas is Enemy* type
	room1->setEnemy(getInstanceOfEnemyById(1));

    rooms.push_back(room1);
}

GameData::~GameData()
{
   
}

Enemy* GameData::getInstanceOfEnemyById(int id) const{
    for(const auto enemy : enemies) {
        if(enemy->getMonsterId() == id) {
            return new Enemy(*enemy);
        }
    }
    return nullptr;
}
//gamedata.h
class GameData
{
    public:
        GameData();
        virtual ~GameData();
        std::vector<Enemy*> getEnemies() const;
        Enemy* getInstanceOfEnemyById(int id) const;

    protected:

    private:
        std::vector<Enemy*> enemies;
        std::vector<Room*> rooms;
};

Normally you would have an abstract base class and inherit from it..
If you have a pointer to a base class you can assign derived objects to it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Character
{
public:
  virtual int get_id() const = 0;
  ~Character()
  {}
};

class Enemy : public Character
{
  int get_id() const override;
};

class Monster : public Enemy
{
  int get_id() const override;
};

class Game
{
  Character* get_by_id(int id) const;
private:
  vector<Character *> characters; // any class derived from Character can be stored here
};


It also would be a better idea to get rid of this pointers and use shared_ptr or unique_ptr.
1
2
3
4
5
6
7
8
Enemy* GameData::getInstanceOfEnemyById(int id) const{
    for(const auto enemy : enemies) {
        if(enemy->getMonsterId() == id) {
            return new Enemy(*enemy);
        }
    }
    return nullptr;
}

if i make Enemy class abstract, then how to use copy constructors of derived classes?
i mind if there is a Monster and Humanoid class derived Enemy

1
2
3
4
5
6
7
8
9
10
11
12
Enemy* GameData::getInstanceOfEnemyById(int id) const{
    for(const auto enemy : enemies) {
        if(enemy->getMonsterId() == id) {
            if(enemy->getType() == 0) {
                     return new Monster(*monster);
            }
           if(enemy->getType() == 1) {
                     return new Humanoid(*humanoid);
           }
    }
    return nullptr;
}

is that okay?
or should i add more copy constructors in base class like this?
1
2
3
4
5
6
Enemy::Enemy(const Monster &enemy) {
    ...
}
Enemy::Enemy(const Humanoid &enemy) {
    ...
}
Last edited on
Is there anything in Enemy that needs a copy constructor? Is there anything that won't be properly copied by the default copy constructor?
Why do you want to return a new Monster or Humanoid ?
@Learner2
std::vector<Enemy*> enemies;
is a vector of template enemies, i want to have multiple of them with same parameters i mean return the new object with the same values of each if i need them by id.
Okay i wrote sample of code from the beginning and i'll tell what i want to achieve:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//main.cpp
int main()
{
    GameData gd();
    Enemy* enemy1 = gd.getInstanceOfEnemy(1);
    if(enemy1 != nullptr) {
        enemy1->attack();
    }
    std::cout << "Hello world!" << std::endl;
    return 0;
}

//enemy.h
class Enemy
{
    public:
        Enemy();
        virtual ~Enemy();
        virtual void attack()=0;
    protected:
        int id;
        std::string name;
        int damage;
    private:

};

//enemy.cpp
Enemy::Enemy()
{

}

Enemy::~Enemy()
{

}


//humanoid.h
class Humanoid : public Enemy
{
    public:
        Humanoid();
        Humanoid(int fid, std::string fname, int fdamage);
        virtual ~Humanoid();
        void attack();
    protected:

    private:
};

//humanoid.cpp
Humanoid::Humanoid()
{
    //ctor
}

Humanoid::Humanoid(int fid, std::string fname, int fdamage) {
    id = fid;
    name = fname;
    damage = fdamage;
}


Humanoid::~Humanoid()
{
    //dtor
}

void Humanoid::attack() {
    std::cout << "Stabbed you with a dagger" << std::endl;
}

//monster.h
class Monster : public Enemy
{
    public:
        Monster();
        Monster(int fid, std::string fname, int fdamage);
        virtual ~Monster();
        void attack();
    protected:

    private:
};

//monster.cpp
Monster::Monster()
{
    //ctor
}

Monster::Monster(int fid, std::string fname, int fdamage) {
    id = fid;
    name = fname;
    damage = fdamage;
}

Monster::~Monster()
{
    //dtor
}

void Monster::attack() {
    std::cout << "Bites you" << std::endl;
}

//GameData.h
class GameData
{
    public:
        GameData();
        Enemy* getInstanceOfEnemy(int id);
        virtual ~GameData();
    protected:

    private:
        std::vector<Enemy *> enemies;
};

//GameData.cpp
GameData::GameData()
{
    enemies.push_back(new Monster(1, "Monster1", 20));
    enemies.push_back(new Humanoid(2, "Humanoid1", 30));
}

GameData::~GameData()
{
    //dtor
}

Enemy* GameData::getInstanceOfEnemy(int id) {
/*
    for(const enemy : enemies) {
        if(enemy->getId() == id) {
            return new copy of (Monster / Humanoid) object
        }
    }
*/
    return nullptr;
}

i don't konw where to place copy constructors, and how to use them to get copy object of enemies vector in getInstanceOfEnemy(int id) where enemies vector has types of Monster, Humanoid cause i can't use copy constructor of Enemy class since it's abstract class
Last edited on
You don't need copy ctors since none of enemy, monster or Humanoid has any pointers.
Don't forget to delete your pointers in GamaData dtor.
In GameData just return the pointer from enemies - if make a copy you will have objects with the same id
1
2
3
4
5
6
7
8
9
10
11
Enemy* GameData::getInstanceOfEnemy(int id) 
{
    for(auto enemy : enemies) 
   {
        if(enemy->getId() == id) 
       {
           return enemy;
        }
    }
    return nullptr;
}
@Learner2
But actually i want multiple objects with the same id, i'm just using id to get exact type of object don't worry about id, it does not have to be unique. Also every enemy is a pointer itself and i want to copy this object. I don't want to get pointer to enemy in enemies vector but new object created and returned based on the object in the vector.
enemies vector is a template of objects i want to copy and return pointers to those copies on the heap
Last edited on
You need to implement a virtual member function that polymorphically copies the derived object. Typically this member function is named clone or copy.

Returning and storing pointers will require you to keep track of object identity and handle memory management. Those are two extra nontrivial concerns that can be avoided.

Consider wrapping the pointer up in a class responsible for its ownership, and give that class value semantics:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <memory>
#include <iostream>
#include <vector>

class enemy
{
  class interface
  {
  public:
    virtual ~interface() = default;
    virtual std::unique_ptr<interface> clone()  const = 0;
  
    virtual void attack() const = 0;
  };
  
  template <typename T>
  class implementation : public interface
  {
    T data_;
    
  public:
    implementation(T x): data_(std::move(x)) {}
    std::unique_ptr<interface> clone() const override 
    { return std::make_unique<implementation<T>>(data_); }
    
    void attack() const override { data_.attack(); }  
  };  
  
  std::unique_ptr<interface> pdata_;
  
public:
  template <typename T> 
  enemy(T t): pdata_(std::make_unique<implementation<T>>(std::move(t))) {}

  enemy& operator=(enemy const& that)
  {
    pdata_ = std::move(enemy(that).pdata_);
    return *this;
  }
  enemy(enemy const& that): pdata_(that.pdata_->clone()) {};
  enemy& operator=(enemy&&) noexcept = default;
  enemy(enemy&&) noexcept = default;
  
  void attack() const { pdata_->attack(); }
};

struct monster
{
  void attack() const { std::cout << "the monster bites you\n"; }   
};

struct humanoid
{
  void attack() const { std::cout << "the humanoid shoots you\n"; }   
};

int main()
{
  std::vector<enemy> enemies;
        
  monster  m; 
  humanoid h;
  
  enemy x = m;
  x = h;
  
  enemies.push_back(std::move(m));
  enemies.push_back(std::move(h));
  enemies.push_back(std::move(x));
    
  for (enemy e: enemies) e.attack();
}
Last edited on
Thanks mbozzi, that's exactly what i was looking for.
Topic archived. No new replies allowed.