class 层次结构的哈希值 table

Hash table of class hierarchy

我有以下情况:

class A {
public:
 int a;

 // Just for RTTI
 virtual ~A() = default;
}

class B : public A {
public:
 int b;
}

然后我创建了一个 std::unordered_map<int, A>,我希望能够在其中存储 As 和 Bs,并返回到它们的原始表示形式。

我总是知道该元素是对应于 A 还是 B 来自 find() 点的上下文,只需查看键即可。

但是,如果我运行下面的程序:

#include <unordered_map>
#include <iostream>

using namespace std;


class A {
public:
 int a{};

 A() = default;
 A(int a) : a(a){}

 // Just for RTTI
 virtual ~A() = default;
};

class B : public A {
public:
 B(int a, int b) : A(a), b(b) {}
 int b;
};


int main()
{
    std::unordered_map<int, A> m;
    m[1] = A(4);
    m[2] = B(2,5);

    if (dynamic_cast<B *>(&m.at(2)) == nullptr) {
        std::cout << "FAIL!" << std::endl;
    }
}

我的屏幕上打印了 "FAIL"。这不是我期望的行为;我怎样才能让它工作?

您 运行 进入的是 object slicing -- 特别是,您的 unordered_set<int, A> 只能包含类型 A 的对象。当您尝试将 B 类型的对象放入其中时,它会编译,但实际上只有 B 对象的超类部分被复制到数据结构中——子类部分(即部分不属于 A 超类的 B 对象)被 "sliced off" 丢弃,存储在集合中的只是另一个 A.

如果您希望 unordered_set 包含不同子类的值,那么唯一的方法是通过某种指针间接存储值对象;例如,您可以改用 unordered_set<int, A*>,或者更好的是 unordered_set<int, shared_ptr<A> >(这样您就可以避免内存泄漏)。然后,您需要在堆上分配每个 A/B 对象,然后再将其插入集合中。

(另一种方法是为每个子类保留一个单独的 unordered_set,例如有一个 unordered_set<int, A> 一个 unordered_set<int, B>。当然,这样做的缺点是它会使您的调用代码复杂化)

以防万一将来有人遇到这个问题,您可以采取的另一条路是使用 std::variant 并保留单个散列 table 而不是 - 至少直接 - 使用 Jeremy 建议的指针.