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


// style 1Widget obj = func(params);// style 2Widget obj;func(&obj, params);但是,能达到同样的目的,消耗的成本却未必是一样的,这取决于多个因素 , 比如编译器支持的特性、C++语言标准的规范强制性等等 。
看起来style 2虽然需要写两行代码,但函数内部的成本却是确定的,只会取决于你当前的编译器,外部即使采用不同的编译器进行函数调用,也并不会有多余的时间开销和稳定性问题 。使用style 1时,较复杂的函数实现可能并不会如你期望的使用RVO优化 , 如果编译器进行RVO优化,使用style 1无疑是比较好的选择 。利用好编译器RVO特性,也是能为程序带来一定的性能提升 。
函数传参使用对象的引用
effective C++中也提到了:以pass-by-reference-to-const替换pass-by-value
指在函数参数传递时 , 将原本使用"pass-by-value"(按值传递)的方式改为使用 "pass-by-reference-to-const"(按常量引用传递)的方式 。
在 "pass-by-value" 中,函数参数会创建一个副本,而在 "pass-by-reference-to-const" 中,函数参数会成为原始对象的一个引用,且为了避免修改原始对象 , 使用了常量引用 。
通过使用 "pass-by-reference-to-const",可以避免在函数调用时进行对象的拷贝操作,从而提高程序的性能和效率;还可以避免对象被切割问题:当一个派生类对象以传值的方式传入一个函数 , 但是该函数的形参是基类,则只会调用基类的构造函数构造基类部分 , 派生类的新特性将会被切割 。此外,使用常量引用还可以确保函数内部不会意外地修改原始对象的值 。
std::shared_ptr线程安全对shared_ptr相信大家都很熟悉,但是一提到是否线程安全,可能很多人心里就没底了 , 借助本节 , 对shared_ptr线程安全方面的问题进行分析和解释 。shared_ptr的线程安全问题主要有两种:1. 引用计数的加减操作是否线程安全; 2. shared_ptr修改指向时是否线程安全 。
引用计数
shared_ptr中有两个指针,一个指向所管理数据的地址 , 另一个指向执行控制块的地址 。
执行控制块包括对关联资源的引用计数以及弱引用计数等 。在前面我们提到shared_ptr支持跨线程操作,引用计数变量是存储在堆上的,那么在多线程的情况下 , 指向同一数据的多个shared_ptr在进行计数的++或--时是否线程安全呢?
引用计数在STL中的定义如下:
_Atomic_word _M_use_count;   // #shared_Atomic_word _M_weak_count;  // #weak + (#shared != 0)当对shared_ptr进行拷贝时,引入计数增加,实现如下:
template <>inline bool _Sp_counted_base<_S_atomic>::_M_add_ref_lock_nothrow() noexcept {    // Perform lock-free add-if-not-zero operation.    _Atomic_word __count = _M_get_use_count();    do {        if (__count == 0) return false;        // Replace the current counter value with the old value + 1, as        // long as it's not changed meanwhile.    } while (!__atomic_compare_exchange_n(&_M_use_count, &__count, __count + 1, true, __ATOMIC_ACQ_REL,                                          __ATOMIC_RELAXED));    return true;}template <>inline void _Sp_counted_base<_S_single>::_M_add_ref_copy() {    ++_M_use_count;}对引用计数的增加主要有以下2种方法:_M_add_ref_copy函数,对_M_use_count + 1,是原子操作 。_M_add_ref_lock函数 , 是调用__atomic_compare_exchange_n``实现的``,主要逻辑仍然是_M_use_count + 1,而该函数是线程安全的,_M_add_ref_copy的区别是对不同_Lock_policy有不同的实现 , 包含直接加、原子操作加、加锁 。
因此我们可以得出结论:在多线程环境下,管理同一个数据的shared_ptr在进行计数的增加或减少的时候是线程安全的,这是一波原子操作 。


推荐阅读