所以我对C ++真的很生疏,并且正在尝试实现一个紧凑的映射容器作为练习。我试图找出让我的班级为动态分配的数组选择最佳/最小类型的最佳方法。此外,这种类型可能会在对象的生命周期内发生变化。
想法是将哈希表分解为表和数据:
table: [-1, -1, 2, -1, 1, 0]
data: [ [h0, key0, value0], [h1, key1, value1], [h2, key2, value2] ]
在执行查找时,您对键%表大小进行哈希处理,并在数据数组中获取相应的索引。通过保持实际的哈希表稀疏且尽可能小和轻巧,可以获得良好的缓存速度等。此外,可以按顺序迭代元素。
为了使表较小,我希望它使用尽可能小的数据类型来保存索引(具有最大的余量以最大程度地减少冲突):
随着添加更多条目,表数组将需要调整大小并最终更改类型。我正在尝试找出在容器中执行此操作的最佳方法。
由于我将不得不在定义中使用表数组类型,因此我将不得不使用某种多态性。我认为模板不会起作用,因为类型可以更改并且在运行时不会被知道。
我已经阅读了一些有关联合和变体的信息,但据我了解,它们并不起作用。
我有点C的知识,但是我知道在C ++中,使用void指针的想法很普遍。
我想出的最好的方法是告诉我的容器table
数组都支持相同的接口。但是似乎我要复制很多代码,并为我想尽可能简单和快速地插入一些虚函数查找。
template <typename K, typename V>
struct Entry {
int hash;
V value;
K key;
};
class Table {
public:
virtual int operator[](int) =0;
}
class CharTable: public Table {
public:
CharTable(int s) :t{new char[s]}{}
int operator[](int i) { return t[i]; }
~CharTable() {delete t[];}
private:
char* t;
}
// short table etc...
template <typename K, typename V>
class CompactMapping {
public:
CompactMapping();
V& operator[](const K&);
unsigned int size() const {return sz;}
void resize(unsigned int);
private:
vector<Entry<K,V>> entries;
unsigned int sz;
Table* table;
int allocated;
}
template <typename K, typename V>
V& CompactMapping<K, V>::operator[](const K& key){
//simplified
int index = table[hash(key) % allocated];
if (entries[index].key == key){
return entries[index].value;
}
}
template <typename K, typename V>
void CompactMapping<K, V>::resize(unsigned int s){
if (s <= 2**7)
CharTable* t = new CharTable(s);
if (s <= 2**15)
ShortTable* t = new ShortTable(s);
//... maybe a switch statement instead
for (int i=0; i!=sz; ++i)
t[entries[i].hash %s] = i;
delete *table;
table = t;
allocated = s;
}
完整披露我实际上并未对此进行测试,因此可能无法实现。我只是想知道在我走这条路之前,我的想法是否还可以,或者是否有更好的解决方案。
我也很感谢你们能给我其他建议。
答案 0 :(得分:1)
class CharTable: public Table
您可能想要这个:
template <class Index> class TypedTable : publlic Table { ... };
using CharTable = TypedTable<unsigned char>; // etc
这消除了代码重复。
现在使用虚拟呼叫不会使实现赢得任何速度竞争,但您应该首先进行概要分析。如果虚拟通话存在重大瓶颈,请尝试使用例如switch
语句,看看是否有帮助。使用void
指针是相对良性的,因为它仅限于严格控制的代码段。或者,您可以使用std::variant
或您自己的带标记联合的实现。