C++两类共享数据内存优化方案咨询(附实现代码)
方案评估与优化实现
你的核心思路(用引用计数+哈希表实现数据共享)是正确的,但当前实现存在几个关键缺陷,无法完全满足你的需求,以下是问题分析和改进后的实现:
现有实现的问题
- 引用计数逻辑错误:全局
db用shared_ptr存储数据,这会导致即使所有Model都移除了某条数据的引用,db中的shared_ptr仍会维持Data的生命周期,永远不会触发自动删除,违反需求2。 - 冗余存储:Model的vector中同时存储DB的key和
shared_ptr,既浪费内存,也容易出现key和指针不一致的情况。 - 无封装的全局DB:直接暴露全局
db会导致操作混乱,无法统一管理数据的创建、清理逻辑,多线程场景下还会有线程安全问题。
改进后的实现
核心设计调整
- 改用
weak_ptr存储DB中的数据,让DB仅作为数据的"索引"而非持有者,真正的生命周期由Model中的shared_ptr引用计数控制。 - 封装DB操作,提供统一的
get_or_create方法,确保相同id的数据只创建一次。 - 简化Model的存储结构,只保留数据引用和模型专属元数据。
完整代码实现
#include <vector> #include <unordered_map> #include <memory> #include <mutex> #include <algorithm> #include <string> class Data { public: Data(const int xTemp, const int yTemp, const int idTemp) : x(xTemp), y(yTemp), id(idTemp) {} int getX() const { return x; } int getY() const { return y; } int getId() const { return id; } // 如果需要修改数据,必须加线程安全保护 void setX(int newX) { std::lock_guard<std::mutex> lock(mtx); x = newX; } private: int x; int y; int id; std::mutex mtx; // 多线程修改时的锁 }; class SharedDB { private: std::unordered_map<int, std::weak_ptr<Data>> data_map; std::mutex mtx; // 线程安全锁,单线程场景可移除 public: // 获取或创建数据:相同id复用已有实例,避免重复内存占用 std::shared_ptr<Data> get_or_create(int id, int x, int y) { std::lock_guard<std::mutex> lock(mtx); auto it = data_map.find(id); if (it != data_map.end()) { if (auto valid_ptr = it->second.lock()) { return valid_ptr; } // 清理已过期的weak_ptr data_map.erase(it); } // 创建新数据并加入DB auto new_data = std::make_shared<Data>(x, y, id); data_map[id] = new_data; return new_data; } // 清理所有无引用的过期数据条目 void cleanup_expired() { std::lock_guard<std::mutex> lock(mtx); for (auto it = data_map.begin(); it != data_map.end();) { if (it->second.expired()) { it = data_map.erase(it); } else { ++it; } } } }; // 基类封装通用Model逻辑,避免代码重复 template<typename ModelType> class BaseModel { protected: struct Entry { std::shared_ptr<Data> data; std::string model_specific_id; // 模型专属元数据 }; std::vector<Entry> entries; SharedDB& db; public: explicit BaseModel(SharedDB& db_instance) : db(db_instance) {} // 插入数据:自动复用已有id的实例 void insert(int data_id, int x, int y, const std::string& model_id) { auto data_ptr = db.get_or_create(data_id, x, y); entries.push_back({data_ptr, model_id}); } // 从当前Model中移除指定id的数据 void remove_by_data_id(int data_id) { entries.erase( std::remove_if(entries.begin(), entries.end(), [data_id](const Entry& entry) { return entry.data->getId() == data_id; }), entries.end() ); // 触发DB清理过期数据(也可定时调用,避免频繁清理) db.cleanup_expired(); } // 示例:遍历当前Model的所有数据 void traverse() const { for (const auto& entry : entries) { // 处理数据逻辑 } } }; // ModelA专属实现 class ModelA : public BaseModel<ModelA> { public: using BaseModel::BaseModel; // 这里可以添加ModelA特有的方法 }; // ModelB专属实现 class ModelB : public BaseModel<ModelB> { public: using BaseModel::BaseModel; // 这里可以添加ModelB特有的方法 }; // 使用示例 int main() { SharedDB db; ModelA model_a(db); ModelB model_b(db); // 插入相同id的数据,只会创建一次Data实例 model_a.insert(1, 10, 20, "A_001"); model_b.insert(1, 10, 20, "B_001"); // ModelA移除数据,ModelB仍持有引用,Data不会被销毁 model_a.remove_by_data_id(1); // ModelB也移除后,Data引用计数归0,下次cleanup_expired会清理DB中的条目 model_b.remove_by_data_id(1); return 0; }
方案满足需求的验证
- 数据同步与保留:当一个Model移除数据时,只要另一个Model仍持有
shared_ptr,Data的引用计数不为0,不会被销毁;DB仅用weak_ptr观测,不影响生命周期。 - 自动清理无引用数据:当所有Model都释放某Data的
shared_ptr后,Data会被自动销毁,DB中的weak_ptr会过期,调用cleanup_expired时会移除该条目。 - 无数据复制:所有Model持有的都是同一个
shared_ptr指向的Data实例,完全没有复制Data对象,内存仅占用一次。
注意事项
- 如果需要修改Data对象,必须添加线程安全保护(如示例中的
std::mutex),避免多线程访问时出现数据竞争。 cleanup_expired的调用时机可根据实际场景调整:可以在每次移除操作后调用,也可以定时批量清理,平衡性能和内存占用。- 避免循环引用:当前设计中DB用
weak_ptr,Model用shared_ptr,不存在循环引用问题,不会导致内存泄漏。
内容的提问来源于stack exchange,提问作者Vlad Shlimovich
相关产品推荐
相关产品推荐

