Home > Back-end >  Iterate through a vector of shared_ptr
Iterate through a vector of shared_ptr

Time:12-09

I have two classes (sheep and wolf) derivating from one (animal). I created a vector of shared pointer like that:

std::vector<std::shared_ptr<animal>> animals;

for (int i = 0; i < n_sheep; i  ) {
    auto my_sheep = std::make_shared<sheep>();
    animals.push_back(std::move(my_sheep));
  }

  for (int i = 0; i < n_wolf; i  ) {
    auto my_wolf = std::make_shared<wolf>();
    animals.push_back(std::move(my_wolf));
  }

What is the best way to iterate through this vector and to know the difference between those smart_ptr ?

Something like that:

for (int i = 0; i < animals.size(); i  ) {
    if (animals[i] == sheep)
        //TODO
    if (animals[i] == wolf)
        //TODO

Thank you all.

CodePudding user response:

"The best" depends on several things. For example, who controls the definition of the class Animal?

I really like to do something akin to:

class Animal
{
public:
  virtual std::string what() = 0;
};

class Wolf : public Animal
{
public:
  virtual std::string what() { return "wolf"; }
};
class Sheep : public Animal
{
public:
  virtual std::string what() { return "sheep"; }
};

You can also define an enum to get rid of the need to compare strings, but of course, this would mean that you will lose the ability to add more animals without editing that enum.

If you are unable to alter Animal, you can just do something like:

if( typeid(*animals[i]) == typeid(Wolf) )
{
  // do wolfy things
}
else if( typeid(*animals[i]) == typeid(Sheep) )
{
  // do sheepish things
}

CodePudding user response:

You can use dynamic_pointer_cast to tell one derivative class from another. It may be not the best way, but it works even if you cannot add new virtual methods to the base.

for (auto animal: animals){
  auto p = std::dynamic_pointer_cast<wolf>(animal);
  if(p) std::cout << "wolf"; // p will be null if it is not wolf
  else std::cout << "sheep"; // if you have only two animals,
                             // otherwise you need to cast to sheep
                             // to check if it is actually sheep
}

CodePudding user response:

As a variation on @v010dya's approach (and on the assumption you are free to modify Animal), I would (as mentioned) use an enum. If you choose hex values you can use bitwise operators which makes comparison simpler (though relies on implicit conversion to int).

However, I'd split this logic into a 'relationship' class. The issue is that you don't really want to pollute Animal with the knowledge of derived classes, but you do need to have a way for different derived classes to interact.

Here, I've used Menagarie as a class which controls the relationships between different Animals. In this way, Wolf doesn't need to know anything about Sheep except that it is an Animal it can eat. You can use this pattern for other interactions between Animals of different types.

#include <iostream>

//Fwd definition of Animal
class Animal;

//This class controls the relationships between animals
class Menagerie
{
public:
    enum Type { NONE = 0x00, SHEEP = 0x01, GOAT = 0x02, WOLF = 0x04, LION = 0x08 };
    //Defines the food chain
    static bool canBeEatenBy(const Animal* pLunch, const Animal* pDiner);
};

class Animal
{
public:
    virtual Menagerie::Type type() const = 0;
    virtual void DoSomething() = 0;
  
    virtual ~Animal() {}
};

class Sheep : public Animal
{
public:
    Menagerie::Type type() const override { return Menagerie::SHEEP; }

    void DoSomething() override
    {
        //Do Sheep-specific things
    }
};

class Wolf : public Animal
{
public:
    Menagerie::Type type() const override { return Menagerie::WOLF; }

    void DoSomething() override
    {
        //Do Wolf-specific things
    }
};

//This defines the logic of who-eats-who
//Can be moved to implementation file, so you can 
//change the logic in one place with minimal recompilation
bool Menagerie::canBeEatenBy(const Animal* pLunch, const Animal* pDiner)
{
    switch (pLunch->type())
    {
    case SHEEP:
        return pDiner->type() & (WOLF | LION);
    case GOAT:
        return pDiner->type() & (WOLF | LION);
    case WOLF:
        return pDiner->type() & LION;
    case LION:
        return false;
    default:
        return false;
    }
}

int main() 
{
    Wolf w;
    Sheep s;

    if (Menagerie::canBeEatenBy(&s, &w))
    {
        std::cout << "Lunchtime for Wolf!\n";
    }

    if (Menagerie::canBeEatenBy(&w, &s))
    {
        std::cout << "Lunchtime for Sheep!\n";
    }
}
  •  Tags:  
  • c
  • Related