C++对象变更自动写入/通知容器实现:问题与优化求助
问题解答:带变更通知的C++容器实现与优化
一、是否存在现成的C++容器?
C++标准库没有原生提供这种支持变更自动通知/持久化的容器,但可以通过两种方式实现需求:
- 基于标准容器封装(即你当前的实现思路),轻量且可控
- 第三方库方案:比如Qt的
QObject派生类结合容器(如QVector+信号槽)、Boost.Signals2配合容器做通知机制,但依赖第三方库会增加项目复杂度
二、编译错误解决(map初始化问题)
你的编译错误源于多余的构造函数重载:
当用std::map的初始化列表(如{{"a",1}, {"b",2}})时,编译器匹配到带std::initializer_list<U>的构造函数,但std::map没有map(initializer_list, 空参数)的构造版本,导致初始化失败。
解决方法:直接删除第二个构造函数,即移除这段代码:
template<class U, class... Args> explicit StatVar(std::shared_ptr<S> const& storage, std::initializer_list<U> ilist, Args&&... args) : m_var{ilist, std::forward<Args>(args)...}, m_storage{storage} {}
第一个模板构造函数template<class... Args> StatVar(...)会自动推导初始化列表参数,正确完成std::map的初始化。
三、现有实现的Bug与优化方案
已发现的核心Bug
- Modificator拷贝风险:未禁用拷贝构造/赋值,多个
Modificator实例会重复触发write(),甚至引发数据竞争 - const operator[]非法:
std::map的operator[]是非const方法(会插入默认元素),你的const版本operator[]会编译失败 - 全量比对效率低下:析构时用
m_stat_var.m_var != m_var做全量比对,大容器(如大vector、map)开销极大 - 线程安全漏洞:
modyfy()中的锁在返回Modificator后就会释放,析构时写回数据未加锁,存在数据竞争
优化方案
禁用Modificator拷贝:在
Modificator类中添加:Modificator(const Modificator&) = delete; Modificator& operator=(const Modificator&) = delete; Modificator(Modificator&&) = default; Modificator& operator=(Modificator&&) = default;用修改状态替代全量比对:在
Modificator中添加bool m_modified标志,任何修改操作触发时设置标志,析构时仅检查标志:class Modificator { public: Modificator(StatVar<S, C>& stat_var): m_stat_var{stat_var}, m_var{m_stat_var.m_var}, m_modified(false) {} ~Modificator() { if (m_modified) { std::unique_lock lock(m_stat_var.m_mutex); // 重新加锁保证线程安全 m_stat_var.m_var = m_var; m_stat_var.m_storage->write(); } } // 所有修改操作都设置m_modified template <typename K> auto& operator[](const K& key) { m_modified = true; return m_var[key]; } C& operator *() { m_modified = true; return m_var; } C* operator->() { m_modified = true; return &m_var; } private: StatVar<S, C>& m_stat_var; C m_var; bool m_modified; };修复const operator[]:改用
at()方法实现const版本的索引访问,避免插入默认元素:template <typename K> auto const& operator[](const K& key) const { std::shared_lock lock(m_mutex); return m_var.at(key); }修正拼写错误:将
modyfy()改为modify(),符合英文拼写规范。
四、map索引访问的实现
删除多余构造函数后,StatVar<TestStorage, std::map<std::string, int>>的初始化会正常工作,m.modify()["test4"] = 4;会调用Modificator的operator[],直接实现map的索引修改。
修改后的完整可运行代码
#include <iostream> #include <string> #include <vector> #include <map> #include <memory> #include <mutex> #include <shared_mutex> class TestStorage { public: void write() { std::cout << "write data to Storage" << std::endl; } }; template<class S, class C> class StatVar { public: template<class... Args> explicit StatVar(std::shared_ptr<S> const& storage, Args&&... args) : m_var{std::forward<Args>(args)...}, m_storage{storage} {} class Modificator { public: Modificator(StatVar<S, C>& stat_var): m_stat_var{stat_var}, m_var{m_stat_var.m_var}, m_modified(false) { std::cout << "begin modify" << std::endl; } ~Modificator() { std::cout << "end modify" << std::endl; if (m_modified) { std::cout << "object is modified" << std::endl; std::unique_lock lock(m_stat_var.m_mutex); m_stat_var.m_var = m_var; m_stat_var.m_storage->write(); } } // 禁用拷贝,允许移动 Modificator(const Modificator&) = delete; Modificator& operator=(const Modificator&) = delete; Modificator(Modificator&&) = default; Modificator& operator=(Modificator&&) = default; template <typename K> auto& operator[](const K& key) { std::cout << "index access" << std::endl; m_modified = true; return m_var[key]; } C& operator *() { std::cout << "reference access" << std::endl; m_modified = true; return m_var; } C* operator->() { std::cout << "pointer access" << std::endl; m_modified = true; return &m_var; } Modificator& operator=(const C& other) { std::cout << "assignment access" << std::endl; m_modified = true; m_var = other; return *this; } private: StatVar<S, C>& m_stat_var; C m_var; bool m_modified; }; operator C const&() const { std::shared_lock lock(m_mutex); return m_var; } C const* operator->() const { std::shared_lock lock(m_mutex); return &m_var; } C const& operator*() const { std::shared_lock lock(m_mutex); return m_var; } template <typename K> auto const& operator[](const K& key) const { std::shared_lock lock(m_mutex); return m_var.at(key); } Modificator modify() { std::unique_lock lock(m_mutex); return Modificator(*this); } private: C m_var; friend class Modificator; std::shared_ptr<S> m_storage; mutable std::shared_mutex m_mutex; }; int main() { using namespace std::string_literals; std::shared_ptr<TestStorage> storage{new TestStorage()}; StatVar<TestStorage, int> i(storage, 0); i.modify() = 10; std::cout << "new i: " << i << std::endl; std::cout << "end" << std::endl; StatVar<TestStorage, std::string> s(storage, "test"); s.modify()->push_back('1'); s.modify()[s->size() - 1] = '0'; std::cout << "new size: " << s->size() << std::endl << "new data: " << static_cast<const std::string&>(s) << std::endl; StatVar<TestStorage, std::vector<std::string>> v(storage, {"test1"s, "test2"s, "test3"s}); v.modify()->push_back("test"); v.modify()[v->size() - 1] = "test4"; std::cout << "new size: " << v->size() << std::endl << "new data: " << static_cast<const std::string&>(v[v->size() - 1]) << std::endl; StatVar<TestStorage, std::map<std::string, int>> m(storage, {std::pair<std::string, int>{"test1"s, 1}, std::pair<std::string, int>{"test2"s, 2}, std::pair<std::string, int>{"test3"s, 3}}); m.modify()->insert({"test4"s, 0}); m.modify()["test4"s] = 4; std::cout << "new size: " << m->size() << std::endl << "new data: " << static_cast<const int&>(m->at("test4"s)) << std::endl; return 0; }
内容的提问来源于stack exchange,提问作者Maxim
相关产品推荐
相关产品推荐

