如何让对象知道它所在的容器?

时间:2015-09-03 21:33:04

标签: c++ c++11 stl containers unordered-set

我需要一种方法将各种物品放入套装中。每个项目一次只能设置1个。我还需要能够询问项目所在的项目。我有3个问题:

这是有效的实施吗?

如果我将类拆分成多个头文件,我怎么能使用它,因为每个类需要另一个?

我可以在项目类中使用引用而不是指向Manager的指针(因为我不喜欢指针)吗?

class Manager {
public:
  add_item(Item& item) {
    items.insert(&item);
    item.manager = this;
  }
  remove_item(Item& item) {
    items.erase(&item);
    item.manager = nullptr;
  }
private:
  std::unordered_set<Item*> items;
}

class Item {
public:
  Manager* get_manager() {return manager;}
private:
Manager* manager;
}

4 个答案:

答案 0 :(得分:1)

  

这是有效的实施吗?如果我拆分,我该如何使用它   将类分成多个头文件,因为每个类都需要   其他

你应该使用前向声明。

在良好的面向对象设计中,您的对象不应该知道它是如何存储的。看起来您的对象不应该对需要本地化对象的活动负责。

std::unordered_list<Item*> items; 

这不合适。您想要散列对象的地址吗?

  

我可以在项目类中使用引用而不是指向Manager的指针   (因为我不喜欢指针)?

传递给函数时总是可以用引用替换指针。它没有问题。

答案 1 :(得分:1)

  

如何让对象知道它所在的容器?

你不应该在适当的班级设计中这样做。有Dependency Injection之类的东西,但我认为这超出了你的实际需要。

  

这是一个有效的实施吗?

没有

  

如果我将类拆分成多个头文件,我怎么能使用它,因为每个类需要另一个?

使用前向声明(有关详细信息,请查看here)。

  

我可以在项目类中使用引用而不是指向Manager的指针(因为我不喜欢指针)吗?

不,你不能。

除了实际问题之外,还有更深入的问题,它最终似乎是一个设计问题,而且你要求XY问题。

你不喜欢(原始)指针是一种非常好的胆量感觉,你的实际设计可能有什么问题。好吧,遗憾的是,您无法使用std::unordered_set等标准容器管理引用。

你可以做的是使用Dynamic memory management设施提供的智能指针。

您必须采取的主要决策是,std::shared_pointerstd::weak_ptrstd::unique_ptr等各种智能指针是管理必要 所有权的正确指针之一要求 语义正确。

同样Manager可能不是您想要做的最佳命名选择。请查看经典Design Patterns,如果有更合适的东西。

例如,您需要Observer之类的内容来跟踪多个Item更改/事件,并将这些内容转发给其他已注册的Item实例。

由于Observer不需要拥有任何已注册Item个实例的所有权,因此std::weak_ptr似乎是引用任何实例的正确选择。

答案 2 :(得分:1)

  

我需要一种方法将各种物品放入套装中。每个项目一次只能设置1个。我还需要能够询问项目所在的项目。我有3个问题:

     

这是一个有效的实施吗?

基本思想是合理的,但它不会强制执行您的数据模型或防范有问题的场景。有关您可以做的事情的示例:

void add_item(Item& item) {
    if (item.manager == this) return; // already owner
    if (item.manager) // already has another owner...
        item.manager->remove(item);
    items.insert(&item);
    item.manager = this;
}
  

如果我将类拆分成多个头文件,我怎么能使用它,因为每个类需要另一个?

你为什么要这样?这些类似乎太简单了,没有任何需要,它只会让事情复杂化。 (正确的方法是使用前向声明标题。)

  

我可以在项目类中使用引用而不是指向Manager的指针(因为我不喜欢指针)吗?

不符合您当前的设计,因为从item.Manager移除nullptr后,您的代码会将Item设置为Manager,这是合理的做法。

更一般地说,您需要确保实际Item个对象的生命周期跨越您在Manager对象中存储指针的时间。这可能是也可能不是代码编写方式的自然结果,或者通过在item.manager->remove(item);销毁之前调用Item轻松实现。

答案 3 :(得分:0)

假设不期望Manager拥有Item并且你清楚地知道ItemManager的生命周期,那么这似乎是合理的

如果要将类拆分为单独的头文件Item.h,只需要Manager的前向声明。实际上,Item根本不需要Manager的完整定义,因此您可以将其简化为:

<强> Item.h

class Manager;  // forward declaration

class Item {
public:
  Manager* get_manager() const { return manager; }
  void set_manager(Manager* new_manager) { manager = new_manager; }
private:
  Manager* manager = nullptr;  // Ensure initialized to null
};

<强> Manager.h

#include "Item.h"
#include <unordered_set>

class Manager {
public:
  void add_item(Item& item) {
    if (item.get_manager())
      return;  // Item can have only one Manager
    items.insert(&item);
    item.set_manager(this);
  }
  void remove_item(Item& item) {
    items.erase(&item);
    item.set_manager(nullptr);
  }
private:
  std::unordered_set<Item*> items;
};

Live demo

至于不喜欢指针,如果你想要一个可以为null的非拥有引用,那么指针是一个非常合适的选择。指针通常比成员变量的引用更合适,因为引用限制了您可以对类进行的操作。