使用STL容器,类包含自己的键
Using STL containers with a class containing its own key
我有一个对象,它可以通过其名称来标识,我想把它放在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::string
和MyClass
类,如果我使用它们像stringInstance < MyClassInstance
,这工作得很好,但容器函数是不可用的(我甚至试图重载函数与字符串工作,但没有成功)。
你能建议我一个简单的方法(或一种方式),如何解决这个问题与std::set
或std::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
适配器。添加所需的重载以满足您的需求,模板化参数(分配器、比较、存储类型等)。
- 编译包含字符串的代码时遇到问题
- 如何正确包含我自己的标头?
- 在wxWidgets的事件中包含我自己的数据的最佳方法是什么?
- 如何在自己的仅标头库中包含提升标头
- 如何将另一个项目中用 C 编写的源代码包含在我自己的项目中,C++在 Visual Studio 中
- G++ 切换为不包含自己的符号函数名称(和调试数据)-
- C 程序要读取包含两个双重值的Excel数据的单列,这些值由逗号分隔并存储在自己的数组中
- MinGW g++ 在自己的包含目录中找不到标头
- Python 可以运行脚本的多个实例,每个实例都包含自己的数据吗?
- 如何将 DirectX 库包含在我自己的静态库(独立库)中
- CMakeLists.txt有两个子项目和自己的CMakeLists.txt只需要创建一个.so库,该库包含所有子项目
- Visual Studio 2013 - 如何在我自己的库中链接/包含其他库
- 无法编译.头文件.包含自己的对象定义
- 使用STL容器,类包含自己的键
- 如何将CodeSynthesis XSD包含到自己的应用程序中
- 类可以包含自己的列表吗?
- 应用程序如何在c++中删除自己和包含它的文件夹
- 在它自己的源文件中包含头文件
- 是否需要显式地包含其他标头已经包含它们自己的标头?
- 如何为我自己的集合类启用包含大括号的初始值设定项列表