"onEachSubelement(...)" C++方法

"onEachSubelement(...)" method for C++

本文关键字:方法 C++ onEachSubelement      更新时间:2023-10-16

我经常将一些数据打包到class中,以防止公共/全局访问出错,并提供一些常用的方法,例如:

class GameArea{
    std::vector<Enemy*> enemies;
    std::wstring name;
public:
    void makeAllEnemiesScared();
    Enemy * getEnemy(unsigned index);
};

GameArea这里只是一个简化的例子。它是一种具有一些专门方法的自定义容器/动物园(但它不仅仅是一个容器(。

理想的情况是,当我知道将同时在每个Enemy上执行哪些操作并且它们发生在多个位置时,因此我可以直接在GameArea中声明它们(如makeAllEnemiesScared()(。

对于其他情况,我可以选择:

for(unsigned i=0; i<gameArea->getEnemiesCount(); i++){
    Enemy * enemy = gameArea->getEnemy(i);
    ...
}

但它有一些缺点

  • 我不能使用 C++11 干净漂亮的for(auto &Enemy : enemies)循环,
  • 效率不高(这么多电话getEnemy(index)(,
  • getEnemy(index) 迭代抛出所有元素并不是一个目的 - 当我们想选择单个或几个元素时,它很有用,它还检查内部index < enemies.size() - 在循环中的每个元素上检查它是很糟糕的。

注意:当我做一些非常特别的事情时,我会想到这种情况(不值得在GameArea中创建单独的方法,而是在GameArea::enemies的每个元素上(。

我想到了某种GameArea::onEachEnemy(... function ...)方法,该方法将function(或者更好的是lambda?(作为参数。这是好的解决方案吗?

或者也许这里应该使用不同的方法?就像从GameArea归还std::vector一样 ——这对我来说看起来有点"丑"。我不希望用户认为他实际上可以直接在该vector中添加或删除项目。

如果你公开向量本身,或者至少是迭代器beginend,你可以使用std::for_each

然后你会把它用作

std::for_each(std::begin(enemies), std::end(enemies), foo);  

其中 foo 是您要在每个Enemy上调用的函数。

再次注意,您不必公开向量本身,您可以在GameArea中创建方法来获取迭代器,以便调用可以像

std::for_each(gameArea->GetEnemyBegin(), gameArea->GetEnemyEnd(), foo);  

正如您所建议的,一个不公开类内部的好解决方案是一个为每个对象调用操作的函数:

class GameArea {
 ...
  template<typename Func> void ForEachEnemy(Func f)
  {
    std::for_each(enemies.begin(), enemies.end(), f);
  }
 ...

然后你可以传递任何你想要的东西作为参数ForEachEnemy - 全局函数、boost::bind结果等。

您可能需要考虑迭代器模式。这样,您可以保留封装,但仍然可以使用方便。

您可以公开标准容器迭代器,或者如果您想要完全封装,请自己编写迭代器。如果正确编写迭代器,您甚至可以使用 C++11 基于范围的 for 循环:

#include <vector>
#include <string>
#include <iostream>
struct Enemy{ std::string name; };
class EnemyIterator {
  friend class GameArea;
private:
  std::vector<Enemy>::iterator iterator;
  EnemyIterator(std::vector<Enemy>::iterator it) : iterator(it){}
public:
  EnemyIterator& operator++() { ++iterator; return *this; }
  Enemy& operator*() { return *iterator; }
  friend bool operator!=(EnemyIterator lhs, EnemyIterator rhs) {
    return lhs.iterator != rhs.iterator;
  }
};
class GameArea{
  std::vector<Enemy> enemies;
public:   
  EnemyIterator begin() { return EnemyIterator(enemies.begin()); }
  EnemyIterator end() { return EnemyIterator(enemies.end()); } 
  void addEnemy(std::string name) {enemies.push_back(Enemy{name}); }
};
int main() {
    GameArea area;
    area.addEnemy("Kobold");
    area.addEnemy("Wraith");
    area.addEnemy("Ork");
    for (auto& enemy : area)
      std::cout << enemy.name << "n";
}

为了使 C++11 基于范围的 for 循环能够与GameArea一起使用,您需要执行以下操作之一:

  • 定义开始和结束成员函数
  • 在同一命名空间中定义开始和结束非成员函数
  • 专门化标准::
  • 开始和标准::结束