下面就简单分享下自己的想法,更详细的可以参考更专业的书籍。
C++ weak_ptr用法和简析(最后是Boost对应的源码,我会提及一些哈)
https://blog.csdn.net/weixin_39956356/article/details/110141462
缺省的swap由内置std::swap提供,很普通的交换,好似下面的代码
要求: 只需要T支持拷贝构造和拷贝赋值即可
namespce std{
template <typename T>
void swap(T& a, T& b) {
T temp(a);
a = b;
b = temp;
}
};
问题: 为了缩小一个类的大小,往往把真实数据放在其他地方,在类中用指针指向它,这就是所谓的pimpl手法(pointer to implementation),如下,一个简单的案例。
class A{
public:
// 正如上面提到的,需要支持拷贝构造、拷贝赋值
A(const A& rhs);
A& operator=(const A& rhs);
private:
AImpl* pImpl; // 指向真实的数据
};
很显然,如果交换两个A对象的值,仅需要交换AImpl即可。 但是如果使用std::swap,std::swap并不知道只需要交换指针就好了,它不仅复制三个A对象,而且复制三个AImpl对象,这是很低效的!!!
回顾std::swap的实现,我们可以确定std::swap是异常安全的!
下面是解决方案,针对pimpl手法,提高swap的速率。Boost中weak_ptr也是使用了这种技术
模板类
游戏规则如下:
下面以weak_ptr为例,分析如何使用。
分析:是否满足pimpl手法?
很显然,满足,真正的数据并没有在weak_ptr中,仅保存了一个指针而已。
// weak_ptr的源代码,无关的内容都已经删除了
namespace boost
{
template<class T> class weak_ptr
{
private:
// Borland 5.5.1 specific workarounds
typedef weak_ptr<T> this_type;
public:
typedef typename boost::detail::sp_element< T >::type element_type;
// 第一步STEP1 在类中public中提供一个swap成员函数,让它高效的交换两个对象的值
void swap(this_type & other) BOOST_SP_NOEXCEPT {
std::swap(px, other.px); // STEP3
pn.swap(other.pn); // STEP4
}
private:
element_type * px; // contained pointer
boost::detail::weak_count pn; // reference counter
}; // weak_ptr
// 第二步STEP2 在template命名空间实现一个non-member swap,并调用第一步的swap
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) BOOST_SP_NOEXCEPT {
a.swap(b); // 这个swap调用的是第一步public中的swap!!!
}
} // namespace boost
我们一点一点分析
weak_ptr<T> wp1;
weak_ptr<T> wp2;
swap(wp1,wp2); //这个swap怎么工作的呢?
调用顺序:
开始—>STEP2—>STEP1—>STEP3—>STEP4
因为weak_ptr中已经实现了swap,所以按照匹配流程,就不会匹配到全局std::swap
其实这个问题的核心是,你自己写的类有没有swap,如果有就调用它(这里称为Case1)
,如果没有就会匹配全局std::swap(这里称为Case2)
。在自己,我们可以知道
注意:下面是weak_count类下的swap!!!
void swap(weak_count & r) BOOST_SP_NOEXCEPT
{
sp_counted_base * tmp = r.pi_;
r.pi_ = pi_;
pi_ = tmp;
}
这个问题简单,思考一下,px是什么呢?是element_type *
,对于不同的模板参数element_type *
不一样,更不可能穷尽为其都写一个swap,所以就用全局std::swap来进行交换。
针对public的swap而言,下面是一般化的情况
void swap(A& other) xxxx_NOEXCEPT {
using std::swap; // 这个必要
swap(pImpl, other.pImpl)
}
提前引用std::swap
,因此下面的swap,会先到类中寻找,找不到用全局哈。
如下
3. 在类中public中提供一个swap成员函数,让它高效的交换两个对象的值,绝不能抛异常BOOST_SP_NOEXCEPT
4. 在template命名空间实现一个non-member swap,并调用第一步的swap