享元模式的核心思想是通过分离对象的内在状态与外部状态来降低内存消耗。1. 内在状态是可共享且不可变的,如字符的字形或树的模型数据。2. 外部状态是随上下文变化的,如字符的位置或树的坐标。3. 适用场景包括存在大量对象、内存消耗巨大、内在状态可共享、外部状态可分离。4. c++++实现的关键技术点包括确保内在状态不可变、使用享元工厂管理对象池、外部状态通过参数传递、考虑线程安全。5. 实际应用包括游戏开发、文本编辑器、网络协议解析。6. 性能考量上,内存效益显著但可能带来一定cpu开销和代码复杂性。

设计C++的享元模式,核心在于通过共享细粒度对象来显著降低内存消耗。这通常意味着你有一大堆看似独立、实则内在状态高度重复的对象实例,享元模式就是将这些重复的内在状态抽取出来,形成少数可共享的“享元”对象。

实现享元模式,通常需要一个享元工厂(Flyweight Factory)来管理和提供享元对象。我们先定义一个抽象的享元接口,然后是具体的享元类,它包含了对象的内在状态(Intrinsic State),这部分状态是可共享且通常不可变的。接着,外部状态(Extrinsic State)则由客户端在使用享元时传入,这部分状态是与上下文相关的,不可共享。
一个典型的C++实现会包含:
立即学习“C++免费学习笔记(深入)”;

std::map
std::unordered_map
#include <iostream>
#include <string>
#include <map>
#include <memory> // For std::shared_ptr
// 1. 抽象享元接口
class CharacterFlyweight {
public:
virtual ~CharacterFlyweight() = default;
// operation() 接受外部状态 (例如:位置, 颜色)
virtual void display(int x, int y, const std::string& color) const = 0;
};
// 2. 具体享元类
// 这里的内在状态是字符本身 (e.g., 'A', 'B', 'c')
class ConcreteCharacterFlyweight : public CharacterFlyweight {
private:
char characterCode_; // 内在状态:字符代码,这是共享的
public:
ConcreteCharacterFlyweight(char code) : characterCode_(code) {
std::cout << "创建了新的享元对象: " << characterCode_ << std::endl;
}
void display(int x, int y, const std::string& color) const override {
std::cout << "显示字符 '" << characterCode_
<< "' 在 (" << x << ", " << y << ") 处,颜色: " << color << std::endl;
}
};
// 3. 享元工厂
class CharacterFlyweightFactory {
private:
// 享元池,key 是内在状态的标识 (这里是 char),value 是享元对象
std::map<char, std::shared_ptr<CharacterFlyweight>> flyweights_;
public:
std::shared_ptr<CharacterFlyweight> getFlyweight(char key) {
// 查找是否已存在
auto it = flyweights_.find(key);
if (it != flyweights_.end()) {
return it->second; // 返回已存在的享元
} else {
// 如果不存在,创建新的享元并存储
std::shared_ptr<CharacterFlyweight> newFlyweight =
std::make_shared<ConcreteCharacterFlyweight>(key);
flyweights_[key] = newFlyweight;
return newFlyweight;
}
}
// 辅助方法,查看当前有多少个享元对象
size_t getFlyweightCount() const {
return flyweights_.size();
}
};
// 4. 客户端代码 (使用示例)
// int main() {
// CharacterFlyweightFactory factory;
// // 模拟一个文本编辑器,显示大量字符
// std::string document = "Hello World! This is a simple document.";
// int x = 0, y = 0;
// for (char c : document) {
// std::shared_ptr<CharacterFlyweight> charFlyweight = factory.getFlyweight(c);
// // 外部状态:位置 (x, y) 和颜色
// charFlyweight->display(x++, y, "Black");
// }
// std::cout << "\n实际创建的享元对象数量: " << factory.getFlyweightCount() << std::endl;
// // 尽管有许多字符实例,但享元对象只创建了唯一字符的种类
// // (H, e, l, o, , W, r, d, !, T, h, i, s, a, m, p, c, u, n, .)
// // 再次使用已存在的享元
// std::shared_ptr<CharacterFlyweight> h_char = factory.getFlyweight('H');
// h_char->display(100, 50, "Red"); // 并没有创建新的'H'享元
// return 0;
// }从我的经验来看,享元模式的核心精髓在于分离。它把一个对象的状态拆分成两个部分:内在状态和外部状态。内在状态是那些可以被多个对象共享的部分,因为它不随上下文变化,比如一个字符的字形、一个树的模型数据。而外部状态则是随上下文变化的,比如字符在屏幕上的位置、树在场景中的坐标。享元模式的魔力在于,它只为内在状态创建少数几个对象实例,而将外部状态通过参数传递给这些共享的实例,而不是为每个“逻辑对象”都创建完整的实例。
这个模式特别适用于以下场景:

如果对象数量不多,或者内在状态变化频繁、难以共享,那么强行使用享元模式反而会增加系统的复杂性,得不偿失。它不是银弹,而是一种针对特定内存问题的优化手段。
在C++里实现享元模式,有几个地方是需要特别留意的,它们直接关系到模式的有效性和健壮性:
const
std::map
std::unordered_map
std::unordered_map
std::map
std::shared_ptr
getFlyweight
flyweights_
std::mutex
这些技术点,尤其是内在状态的不可变性和工厂的有效管理,是决定享元模式能否成功应用的关键。
享元模式在很多领域都有着非常实际的应用,它不是那种只存在于教科书里的设计模式。我印象最深的就是在游戏开发和文本编辑器里。
关于性能考量,这确实是一个双刃剑。
std::map
std::unordered_map
总的来说,享元模式是一个典型的空间换时间(或者说,空间换取更少的空间)的优化模式。只有当内存瓶颈确实存在,且对象具备高度共享的内在状态时,它才值得被考虑。否则,为了所谓的“设计模式”而使用它,反而可能带来不必要的复杂性和性能下降。
以上就是怎样设计C++的享元模式 共享细粒度对象降低内存消耗的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号