简体   繁体   English

遍历 shared_ptr 的向量

[英]Iterate through a vector of shared_ptr

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?遍历此向量并了解这些 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.谢谢你们。

"The best" depends on several things. “最好的”取决于几件事。 For example, who controls the definition of the class Animal?例如,谁控制 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:如果您无法更改Animal ,您可以执行以下操作:

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

You can use dynamic_pointer_cast to tell one derivative class from another.您可以使用dynamic_pointer_cast将一个衍生 class 与另一个衍生。 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
}

As a variation on @v010dya's approach (and on the assumption you are free to modify Animal), I would (as mentioned) use an enum.作为@v010dya 方法的一种变体(假设您可以自由修改Animal),我会(如前所述)使用枚举。 If you choose hex values you can use bitwise operators which makes comparison simpler (though relies on implicit conversion to int).如果您选择十六进制值,则可以使用位运算符,这使得比较更简单(尽管依赖于隐式转换为 int)。

However, I'd split this logic into a 'relationship' class.但是,我会将此逻辑拆分为“关系”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.问题是您真的不想用派生类的知识污染 Animal,但您确实需要有一种方法让不同的派生类进行交互。

Here, I've used Menagarie as a class which controls the relationships between different Animals.在这里,我使用 Menagarie 作为 class 来控制不同动物之间的关系。 In this way, Wolf doesn't need to know anything about Sheep except that it is an Animal it can eat.这样一来,Wolf 就不需要知道关于 Sheep 的任何事情,只需要知道它是一种可以吃的动物。 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";
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM