C++常见避坑指南( 九 )


2. 多线程环境中,对于同一个std::shared_ptr实例 , 只有访问const的成员函数 , 才是线程安全的,对于非const成员函数,是非线程安全的,需要加锁访问 。
【C++常见避坑指南】首先来看一下 std::shared_ptr 的所有成员函数,只有前3个是 non-const 的,剩余的全是 const 的:
成员函数是否const operator=non-constresetnon-constswapnon-constgetconstoperator、operator->constoperatorconstuse_countconstoperator boolconstuniqueconst 讲了这么多,来个栗子实践下:
ant::Promise<JsAPIResultCode, CefRefPtr<CefDictionaryValue>>XXXHandler::OnOpenSelectContactH5(const JsAPIContext& context, std::shared_ptr<RequestType> arguments) { ant::Promise<JsAPIResultCode, CefRefPtr<CefDictionaryValue>> promise; base::GetUIThread()->PostTask(weak_lambda(this, [this, promise, context, arguments]() {  auto b_executed_flag = std::make_shared<std::atomic_bool>(false);  auto ext_param = xx::OpenWebViewWindow::OpenURLExtParam();  // ...  // SelectCorpGroupContact jsapi的回调  ext_param.select_group_contact_callback = [promise, b_executed_flag](   JsAPIResultCode resCode, CefRefPtr<CefDictionaryValue> res) mutable {    *b_executed_flag = true;    base::GetUIThread()->PostTask([promise, resCode, res]() {     promise.resolve(resCode, res);     });  };  // 窗口关闭回调  ext_param.dismiss_callback = [promise, b_executed_flag]() {   if (*b_executed_flag) {    return;   }   promise.resolve(JSAPI_RESULT_CANCEL, CefDictionaryValue::Create());  };  // ...  xx::OpenWebViewWindow::OpenURL(nullptr, url, false, ext_param); })); return promise;}该段代码场景是一个Jsapi接口,在接口中打开另一个webview的选人窗口 , 选人窗口操作后或者关闭时都需要回调下,将结果返回jsapi 。选人完毕确认后会回调select_group_contact_callback,同时关闭webview窗口还会回调dismiss_callback,这俩回调里面都会回包,这里还涉及多线程调用 。这俩回调只能调用一个 , 为了能简单达到这种效果,作者用std::shared_ptrstd::atomic_bool b_executed_flag来处理多线程同步,如果一个回调已执行就标记下,shared_ptr本身对引用计数的操作是线程安全的,通过原子变量std::atomic_bool来保证其管理的对象的线程安全 。
std::map// 定义数据缓存类class DataCache {private:    std::map<std::string, std::string> cache;public:    void addData(const std::string& key, const std::string& value) {        cache[key] = value;    }    std::string getData(const std::string& key) {        return cache[key];    }};在上述示例中,简单定义了个数据缓存类 , 使用 std::map作为数据缓存,然后提供addData添加数据到缓存,getData从map缓存中获取数据 。一切看起来毫无违和感,代码跑起来也没什么问题,但是如果使用没有缓存的key去getData, 发现会往缓存里面新插入一条value为默认值的记录 。
需要注意的是,如果我们使用 [] 运算符访问一个不存在的键,并且在插入新键值对时没有指定默认值,那么新键值对的值将是未定义的 。因此,在使用 [] 运算符访问 std::map 中的元素时 , 应该始终确保该键已经存在或者在插入新键值对时指定了默认值 。
void addData(const std::string& key, const std::string& value) {    if(key.empty()) return;    cache[key] = value;}std::string getData(const std::string& key) {    const auto iter = cache.find(key);    return iter != cache.end() ? iter->second : "";}sizeof & strlen相信大家都有过这样的经历,在项目中使用系统API或者与某些公共库编写逻辑时,需要C++与C 字符串混写甚至转换,在处理字符串结构体的时候就免不了使用sizeof和strlen,这俩看着都有计算size的能力,有时候很容易搞混淆或者出错 。


推荐阅读