使用STL容器,类包含自己的键

Using STL containers with a class containing its own key

本文关键字:包含 自己的 STL 容器 使用      更新时间:2023-10-16

我有一个对象,它可以通过其名称来标识,我想把它放在STL容器

class MyClass {
public:
    //getters and setters, other functions
private:
    std::string name;
    //other member variables
};

所以一开始我认为使用类映射结构与我的情况无关,因为在这些结构中标识符(键)与类本身是分离的。使用映射时,我必须返回name变量并将复制到类的"外部"(浪费内存且不合逻辑,违反OOP规则)。

我的下一个镜头是使用类集合结构。在这个例子中,我只有key字段,我在这里加载了整个对象。使用此方法时,必须重载<、>和==操作符,以便将对象用作键。我甚至可以为哈希创建一个函子如果我使用unordered_set,效果很好。这里的问题是我不能像使用map那样使用容器函数。这是有效的mapInstance.find("example"),而这不是setInstance.find("example")。我必须创建一个对象,将成员变量name设置为"example",并将其传递给find()函数。这种解决方案的问题是,我的类中的其他成员变量是重复的,而没有使用。我甚至尝试重载<,>和==操作符为std::stringMyClass类,如果我使用它们像stringInstance < MyClassInstance,这工作得很好,但容器函数是不可用的(我甚至试图重载函数与字符串工作,但没有成功)。

你能建议我一个简单的方法(或一种方式),如何解决这个问题与std::setstd::map(可能其他)?在std::map的关键不能是一个引用(据我所知),我不知道如何解决它与std::set

注意:map的关键字段中存储指针的问题是,如果我们改变主意并使用unordered_map代替map,哈希将基于指针计算,而不是基于字符串(哈希函数可以被覆盖,但对于一个简单的任务来说似乎非常复杂)。

谢谢你的帮助!

你应该问问自己你对容器的要求是什么。

需要考虑的是:

  • 容器中通常有多少对象
  • 内存约束是什么
  • 搜索对象的频率和复杂度是可以接受的?

std::map有一些可能与你的类冲突的需求。例如,一旦元素被添加到映射中,键就不允许被更改。但是,您的类可能每次都更改名称。从这一点来看,std::map不能使用对字符串的引用作为键。

在最简单的情况下,可以考虑使用std::list和std::find_if和谓词来检查特殊名称。这将具有O(n)复杂度。

有时候,为了得到想要的结果,你必须依靠现有的条件。我建议您正在寻找的是针对您的用例的专用适配器。

这是一个非常基本的实现,您可以在此基础上进行构建。在许多情况下,使用std::vector获得的缓存局部性将比使用提供类似特性集的标准容器提供更好的性能。

给定一个简单类型和一些辅助操作符:

struct Obj
{
   int key;
   std::string name;
};
bool operator<(const Obj& lhs, const Obj& rhs)
{
   return lhs.key < rhs.key;
}
bool operator==(const Obj& lhs, int rhs)
{
   return lhs.key == rhs;
}
bool operator<(const Obj& lhs, int rhs)
{
   return lhs.key < rhs;
}

我们可以设计一个简单的flat_map类,它提供了map的基本的复杂性保证,但将满足您按键查找对象的需求,而无需构造值类型(就像您需要使用set)。插入增加了复杂度,但查找的复杂度也差不多。如果查找比插入频繁得多(大多数情况下都是这样),这可以很好地工作。

class flat_map
{
public:
   using container_type = std::vector<Obj>;
   // insert object into the set
   // complexity varies based on length of container
   void insert(Obj&& obj)
   {
      container_.emplace_back(std::move(obj));
      std::sort(container_.begin(), container_.end());
   }
   // find with O(log N) complexity    
   container_type::iterator find(int key)
   {
      auto it = std::lower_bound(container_.begin(), container_.end(), key);
      if(it != container_.end() && *it == key)
         return it;
      return container_.end();
   }
private:
   container_type container_;
};

使用例子:

int main()
{
   flat_map obj;
   obj.insert({1, "one"});
   obj.insert({2, "two"});
   obj.insert({3, "three"});
   auto it = obj.find(2);
   std::cout << it->key << ' ' << it->name << 'n';
}

您可以以任何您认为合适的方式扩展flat_map适配器。添加所需的重载以满足您的需求,模板化参数(分配器、比较、存储类型等)。