在C中是否存在一种惯用的方法来防止运行一系列操作导致集合发生变异的情况?

前端之家收集整理的这篇文章主要介绍了在C中是否存在一种惯用的方法来防止运行一系列操作导致集合发生变异的情况?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
假设你有一个包含某种可调用对象集合的类foo. foo有一个成员函数run(),它遍历集合并调用每个函数对象. foo还有一个成员remove(…),它将从集合中删除一个可调用对象.

是否有一个惯用的,RAII风格的后卫,你可以放入foo.run()和foo.remove(…),以便通过调用foo.run()驱动删除
将被推迟到守卫的破坏者开火?它可以用标准库中的东西来完成吗?这个模式有名字吗?

我目前的代码似乎不够优雅,所以我正在寻找最佳实践类型的解决方案.

注意:这不是关于并发性的.非线程安全的解决方案很好.问题在于重新引入和自我引用.

这是一个问题的例子,没有优雅的“延迟删除”警卫.

  1. class ActionPlayer
  2. {
  3. private:
  4. std::vector<std::pair<int,std::function<void()>>> actions_;
  5. public:
  6. void addAction(int id,const std::function<void()>& action)
  7. {
  8. actions_.push_back({ id,action });
  9. }
  10.  
  11. void removeAction(int id)
  12. {
  13. actions_.erase(
  14. std::remove_if(
  15. actions_.begin(),actions_.end(),[id](auto& p) { return p.first == id; }
  16. ),actions_.end()
  17. );
  18. }
  19.  
  20. void run()
  21. {
  22. for (auto& item : actions_) {
  23. item.second();
  24. }
  25. }
  26. };

其他地方:

  1. ...
  2.  
  3. ActionPlayer player;
  4.  
  5. player.addAction(1,[]() {
  6. std::cout << "Hello there" << std::endl;
  7. });
  8.  
  9. player.addAction(42,[&player]() {
  10. std::cout << "foobar" << std::endl;
  11. player.removeAction(1);
  12. });
  13.  
  14. player.run(); // boom

编辑…好吧,这是我通过RAII锁定对象看到的方式.假设递归最终终止(如果不是用户错误),以下应该处理在运行中运行的抛出和重入调用的操作.我使用了缓存的std :: functions,因为在这段代码的实际版本中,addAction和removeAction的等价物是模板函数,它们只能存储在一个vanilla homogeneously类型的容器中.

  1. class ActionPlayer
  2. {
  3. private:
  4.  
  5. std::vector<std::pair<int,std::function<void()>>> actions_;
  6. int run_lock_count_;
  7. std::vector<std::function<void()>> deferred_ops_;
  8.  
  9. class RunLock
  10. {
  11. private:
  12. ActionPlayer* parent_;
  13. public:
  14. RunLock(ActionPlayer* parent) : parent_(parent) { (parent_->run_lock_count_)++; }
  15. ~RunLock()
  16. {
  17. if (--parent_->run_lock_count_ == 0) {
  18. while (!parent_->deferred_ops_.empty()) {
  19. auto do_deferred_op = parent_->deferred_ops_.back();
  20. parent_->deferred_ops_.pop_back();
  21. do_deferred_op();
  22. }
  23. }
  24. }
  25. };
  26.  
  27. bool isFiring() const
  28. {
  29. return run_lock_count_ > 0;
  30. }
  31.  
  32. public:
  33. ActionPlayer() : run_lock_count_(0)
  34. {
  35. }
  36.  
  37. void addAction(int id,const std::function<void()>& action)
  38. {
  39. if (!isFiring()) {
  40. actions_.push_back({ id,action });
  41. } else {
  42. deferred_ops_.push_back(
  43. [&]() {
  44. addAction(id,action);
  45. }
  46. );
  47. }
  48. }
  49.  
  50. void removeAction(int id)
  51. {
  52. if (!isFiring()) {
  53. actions_.erase(
  54. std::remove_if(
  55. actions_.begin(),[id](auto& p) { return p.first == id; }
  56. ),actions_.end()
  57. );
  58. } else {
  59. deferred_ops_.push_back(
  60. [&]() {
  61. removeAction(id);
  62. }
  63. );
  64. }
  65. }
  66.  
  67. void run()
  68. {
  69. RunLock lock(this);
  70. for (auto& item : actions_) {
  71. item.second();
  72. }
  73. }
  74. };

解决方法

通常的方法是创建矢量的副本.但这可能会导致删除的操作再次运行.
  1. void run()
  2. {
  3. auto actions_copy{actions_};
  4. for (auto& item : actions_copy) {
  5. item.second();
  6. }
  7. }

不允许运行已删除操作的其他选项

>如果删除某些操作,则添加bool以存储>使用shared / weak ptr>如果已知当前操作不会被删除,请使用std :: list.

猜你在找的C&C++相关文章