C++中使用unordered_set/map时如何实现类的良好封装?
问题解答
是否可以将struct改为class并添加对应成员
是可以实现的,仅需要解决两个核心编译限制:
- 自定义哈希函数
HashFunction需要访问Node类的私有成员val,可以通过友元声明或者新增公有getter方法解决 - 类内定义持有自身类型的
unordered_set属于不完整类型使用,C++17及以上版本的标准库实现普遍支持该用法,低版本标准可以改用智能指针包装元素避免编译错误
修改后的可运行代码如下:
#include <iostream> #include <unordered_set> using namespace std; // 前置声明Node类 class Node; class HashFunction{ public: size_t operator()(const Node& n) const; }; class Node{ private: int val; // 声明哈希函数为友元,允许访问私有val friend class HashFunction; unordered_set<Node, HashFunction> neighbours; public: Node(int v) : val(v) {} bool operator==(const Node& n) const{ return this->val == n.val; } // 封装邻居操作的成员方法,避免直接暴露内部集合 void add_neighbour(const Node& n) { neighbours.insert(n); } int get_val() const { return val; } const unordered_set<Node, HashFunction>& get_neighbours() const { return neighbours; } }; // 哈希函数的实现要放在Node类定义之后 size_t HashFunction::operator()(const Node& n) const{ return n.val; } int main(){ Node n1(1), n2(2), n3(3), n4(4); n1.add_neighbour(n2); n1.add_neighbour(n3); unordered_set<Node, HashFunction> us; us.insert(n1); us.insert(n2); us.insert(n3); us.insert(n4); for (const auto& node : us){ cout << node.get_val() << " "; } cout << endl; cout << "n1的邻居:"; for (const auto& neighbour : n1.get_neighbours()) { cout << neighbour.get_val() << " "; } cout << endl; return 0; }
保证封装性的最佳实践
- 优先用公有getter替代友元:如果不想将哈希函数设为友元,可以给Node类新增公有的
get_val()方法,哈希函数直接调用公有方法读取值即可,耦合度更低,也不会破坏封装性 - 兼容低版本标准的不完整类型处理:如果使用C++17以下标准,无法直接在类内定义
unordered_set<Node>,可以将容器元素改为std::shared_ptr<Node>,既解决不完整类型编译错误,也能避免存储完整对象带来的拷贝开销和对象切片问题 - 哈希计算和判等逻辑对齐:所有参与
operator==判断的成员字段,都要纳入哈希值的计算逻辑,避免大量哈希冲突降低unordered_set的性能,多字段哈希可以用std::hash组合各个字段的哈希值 - 不直接暴露内部容器成员:不要把
neighbours设为公有,也不要返回非const的引用给外部,提供专门的增删查成员方法操作内部集合,保证内部状态的修改都在类的控制范围内 - 判等函数加const限定:
operator==必须声明为const成员函数,保证unordered_set内部调用时可以操作const的Node对象
内容的提问来源于stack exchange,提问作者Firat Cem Gurbey
相关产品推荐
相关产品推荐

