轻量级享元仅在对象数量达数千以上且存在不可变内在状态时才适用;key须为POD并手写高效哈希;工厂返回const shared_ptr;外在状态必须由客户端传入,享元内部全const。

轻量级享元在 C++ 里不是“越小越好”,而是“只共享真正不变的、高频复用的那部分”——否则 std::shared_ptr 查表开销反而比直接 new 还高。
什么时候该用轻量级享元,而不是直接 new 或对象池
享元不是通用优化手段。它只在以下两个条件同时满足时才值得引入:
- 对象数量达数千以上(比如文本渲染中每个字符一个实例、粒子系统中每粒粒子一个描述符)
- 对象中存在明确可分离的「内在状态」:例如
char+font_size+bold,且这些字段在生命周期内从不修改、不依赖上下文
如果对象只有几十个,或每个实例的 position、color_offset、visible 等字段频繁变化且占内存主体,那就别硬套享元——改用对象池或直接栈分配更高效。
key 类型必须是 POD,且哈希要手写
用 std::string 当 key 是常见性能陷阱。构造、拷贝、哈希计算成本高,且容易因空格/大小写导致逻辑重复。
立即学习“C++免费学习笔记(深入)”;
- 推荐定义轻量结构体作为 key,如
GlyphKey,仅含char、int、bool等 POD 成员 - 必须显式特化
std::hash<GlyphKey>,不能靠编译器自动生成——否则哈希碰撞率高,unordered_map退化为链表 - 哈希函数建议用位运算混合字段,例如:
return (static_cast<size_t>(k.c) << 16) ^ (k.font_size << 2) ^ k.bold;
工厂返回 std::shared_ptr<const Flyweight>,不是引用也不是裸指针
返回引用看似零开销,但前提是对象生命周期绝对可控;裸指针则无法自动管理销毁时机。轻量级享元仍需明确所有权语义:
-
std::shared_ptr<const Flyweight>明确表达“只读共享”,避免误写导致数据竞争 - 工厂内部缓存用
std::weak_ptr更安全,防止循环引用;首次创建后升级为shared_ptr返回 - 不要在
get()里加全局互斥锁——改用std::shared_mutex:读走共享锁,写走独占锁
外在状态必须由客户端传入,且不能缓存在享元内部
这是最容易被违反的一条。一旦把 pos_x、z_index、opacity 塞进享元类,就彻底破坏了共享前提:
- 所有操作接口如
render(const Vec2& pos, float alpha) const必须接收外在参数 - 享元类所有成员变量应声明为
const,构造函数用explicit - 若外在状态是结构体(如
RenderParams),注意它可能阻止函数内联——必要时拆成几个标量参数
真正难的是边界判断:比如“是否粗体”属于内在状态,但“当前是否被选中高亮”属于外在状态——这个分界线一旦划错,整个模式就失效。


















