如何使用c++ std::set作为类的构建块
How to use C++ std::sets as building blocks of a class?
我需要一个满足以下条件的数据结构:
- 存储任意数量的元素,其中每个元素由10个数字度量来描述
- 允许通过任何度量 快速(
- 允许快速(
log n
)插入新元素 - 允许快速(
log n
)去除元素
log n
)搜索元素让我们假设这些元素的构造代价很高。
我想出了下面的计划
- 将所有元素存储在名为DATA的vector中。
- 使用10个
std::sets
,每10个指标一个。每个std:set
都是轻量级的,它只包含整数,这些整数是指向向量DATA
的索引。比较运算符在DATA中"查找"适当的元素,然后选择适当的度量
template< int C >
struct Cmp
{
bool operator() (int const a, int const b)
{
return ( DATA[a].coords[C] != DATA[b].coords[C] )
? ( DATA[a].coords[C] < DATA[b].coords[C] )
: ( a < b );
}
};
vector对象永远不能修改或删除元素。将新元素推回DATA,然后将其索引(DATA.size()-1
)插入到集合(set<int, Cmp<..> >
)中。为了删除一个元素,我在元素中设置了一个标志,表示它已被删除(而不是实际从DATA
vector中删除它),然后从所有十个std::set中删除元素索引。
只要DATA
是全局变量,就可以正常工作。(它还使模板结构体Cmp依赖于一个全局变量,从而在某种程度上滥用了类型系统。)
然而,我无法将DATA
向量和std::set (set<int, Cmp<...> >
)包含在一个类中,然后用std::sets
"索引"DATA
。首先,在外部类内部定义的比较操作符Cmp不能访问外部类的字段(因此它不能评估DATA)。我也不能将vector传递给Cmp构造函数,因为Cmp是由std::set构造的,而std::set需要一个没有参数的构造函数的比较操作符。
我有一种感觉,我正在与c++类型系统对抗,并试图实现类型系统故意阻止我做的事情。(我试图使std::set依赖于仅在运行时构造的变量。)虽然我理解为什么类型系统可能不喜欢我所做的,但我认为这是一个合理的用例。
有没有一种方法来实现我上面描述的数据结构/类,而不提供std::set/红黑树的重新实现?我希望有一个我还没有想到的诀窍。(是的,我知道boost有一些东西,但我想坚持使用标准库。)当我读到"通过值bar
查找foo
"时,我的第一反应是使用map<>
或类似的东西。这里有一些暗示:
-
std::map
中的键(或std::set
中的值)是唯一的,因此没有两个元素可以共享相同的键,因此没有两个数据对象能够具有相同的度量。如果多个数据对象可以具有相同的度量(这从你的问题中不清楚),使用std::multimap
(或std::multiset
)可以工作。 - 如果键是常量并且存储在元素本身中,使用
set<data*,cmp>
是一种常见的方法。然后比较器只从对象中检索相应字段并对它们进行比较。然后查找需要创建一个临时对象并对其使用find()。一些实现还具有允许使用不同类型进行搜索的扩展,这将使搜索变得更加容易,但也使移植需要实际的工作。作为键的字段保持不变是很重要的,因为如果你修改它们,你隐式地改变了set<>
的顺序。这就是set<>
的元素实际上是常量的原因,即即使是普通的iterator
也有一个常量作为值类型。如果你存储指针,你可以很容易地解决这个问题,因为常量指针和指向常量的指针是不同的。别拿那个搬起石头砸自己的脚! - 如果指标不是对象本身的属性(或者您不介意冗余存储它们),使用
std::map
将是一个自然的选择。根据度量,在多个键下存储相同的对象,可以在单独的容器中完成(map<int,data*> c[10];
)。然而,你可以在单个地图中使用例如pair<metric,value>
作为键(map<pair<int,int>,data*> c;
)。 - 使用
vector<>
来存储实际的元素,并且只将它们作为指针或索引引用到map中肯定有效。不过我还是会选择指针,因为这是允许使用集合或映射的上述方法工作的原因。如果不这样做,比较器将不得不存储对容器的引用,而目前它只使用全局DATA容器。将这种方法用于向量是很棘手的,因为它会在增长时重新分配元素,正如您正确指出的那样。我会考虑不同的容器类型,如std::list
或std::deque
。前者也允许擦除元素,但是每个元素的开销更高。后者的每个元素开销相对较低,仅略高于std::vector
。然后,您甚至可以存储迭代器而不是指针,这有助于调试,只要您为此使用"检查过的STL"。尽管如此,你仍然需要做一些手工记录,哪些对象仍然被引用,哪些对象没有。 除了使用单独的容器,您还可以动态地分配元素,尽管这本身有一些开销。如果每个元素的开销不是问题,那么可以使用引用计数的智能指针。如果应用程序是一次性进程,你也可以使用原始指针,让操作系统在退出时回收内存。
注意,我假设不可以存储数据对象的多个副本。如果是这种情况,您还可以使用map<int,data> m[10];
,其中每个映射存储自己的数据对象副本。所有的记账问题都将得到解决,但代价是10倍的开销。
相关文章:
- C++为构建时间获取QDateTime的可靠方法
- 无法在 CLion 中构建 C++ 项目
- 函数向量_指针有不同的原型,我可以构建一个吗
- 如何使用ndk-build.cmd构建Android.so文件
- libssh 的函数在构建 libssh 时无法在 Qt 和 cmake 错误中找到
- 为什么我无法更改"set<set>"循环中的值<int>
- 使用cmake从源代码构建MySQL连接器/C++失败(与以前的声明冲突)
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 对于set上的循环-获取next元素迭代器
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 无法使用Qt Creator在Windows中构建yaml-cpp
- 构建一个由C和C++文件组成的库
- llvm构建器向基本块添加终止符
- 在声明中合并两个常量"std::set"(不是在运行时)
- 有没有办法对std::unordered_set、std::unrdered_map、std::set、std::map
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- 如何跨平台将二进制资源构建到程序中?
- 将 OpenCV 与 CMAKE 中的项目一起构建为第三方库的正确方法
- 将 std::set 与基于键的比较器一起使用
- 如何使用c++ std::set作为类的构建块