享元模式(Flyweight)通过共享技术有效支持大量细粒度对象的复用。在C++里,当系统中存在众多相似对象且占用内存较大时,可以把对象的状态分为内部状态和外部状态,内部状态可共享,外部状态由调用方传入。下面通过一个字符样式例子展示C++设计模式之Flyweight的实现方式。

一、享元模式的核心角色
在C++实现中通常包含以下几个部分:
- Flyweight:抽象享元类,定义共享接口。
- ConcreteFlyweight:具体享元类,保存内部状态。
- FlyweightFactory:享元工厂,负责创建和管理共享对象。
- Client:调用方,维护外部状态并使用享元对象。
二、C++代码实例
以下示例模拟不同字符使用相同字体样式对象,避免重复创建字体样式:
#include <iostream>
#include <string>
#include <map>
#include <memory>
// 抽象享元:字体样式
class FontStyle {
public:
virtual void render(char c, int x, int y) = 0;
virtual ~FontStyle() {}
};
// 具体享元
class ConcreteFontStyle : public FontStyle {
private:
std::string fontName; // 内部状态,可共享
public:
explicit ConcreteFontStyle(const std::string& name) : fontName(name) {}
void render(char c, int x, int y) override {
std::cout << "字符 '" << c << "' 使用字体 "
<< fontName << " 绘制于 (" << x << "," << y << ")n";
}
};
// 享元工厂
class FontFactory {
private:
std::map<std::string, std::shared_ptr<FontStyle>> pool;
public:
std::shared_ptr<FontStyle> getFont(const std::string& name) {
if (pool.find(name) == pool.end()) {
pool[name] = std::make_shared<ConcreteFontStyle>(name);
}
return pool[name];
}
};
// 客户端使用
int main() {
FontFactory factory;
auto fontA = factory.getFont("Arial");
auto fontB = factory.getFont("Arial"); // 共享同一个对象
fontA->render('H', 10, 20); // 外部状态 x,y 由调用方传入
fontB->render('i', 30, 20);
return 0;
}
三、内部状态与外部状态
在上面的代码中,fontName 是内部状态,由 ConcreteFontStyle 保存并通过工厂共享;而字符本身及坐标 x、y 是外部状态,由 render() 函数参数传入,不保存在享元对象中。
使用注意点
- 享元模式适合对象数量多且内部状态可提取的场景。
- 线程安全:若多线程使用工厂,需要对
pool的访问加锁。 - 不要滥用,仅当内存占用成为瓶颈时考虑引入。
四、小结
通过C++实现Flyweight模式,我们可以用一个工厂集中管理可共享的对象,显著降低重复内存消耗。掌握内部状态与外部状态的划分,是写好享元模式的关键。