转载自:

   
 shared_ptr是叁个最像指针的“智能指针”,是boost.smart_ptr库中最有价值、最重大的组成都部队分,也是最得力的。

    使用对象来保管能源,可防止止因个体大意带给的有些低端错误,可是或不是每件事都以胜利的。

谢谢原来的著作者,本身也被忧愁了整二个晚间。

   
 shared_ptr达成的是援用计数的智能指针,能够被放肆拷贝和赋值,在恣意的地点分享它,当未有代码应用(引用计数为0)它时才删除被装进的动态分配的目的。shared_ptr也得以悠闲自在地停放标准容器中。

    一些函数依旧选拔原本的财富对象,那么我们就必要为那些函数提供贰个接口,让他俩能够取获得原始对象。

+++++++++++++++++++++++++++++++++++

示例1:

    继续拿13节的智能指针说事,先上代码:

近年来开端拜读侯捷先生和孟岩先生的译作《C++标准程序库:自修教程与参谋手册》。两位先生确实译功上乘,读得很顺。然则读到P55页关于auto_ptr_ref的座谈,却大惑不解:为何须求引进auto_ptr_ref那么些帮助类呢?

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <cassert>
using namespace boost;
using namespace std;
int main()
{
shared_ptr<int> sp(new int(10卡塔尔(قطر‎卡塔尔(قطر‎;    
 //贰个针对整数的shared_ptr
澳门葡萄京官方网站 ,assert(sp.unique());                
//现在shared_ptr是指针的唯大器晚成全体者
shared_ptr<int> sp2 = sp;            
//第二个shared_ptr,拷贝布局函数
assert(sp == sp2 && sp.use_count() == 2);
*sp2 = 100;                 //使用解引用操作符改革被指目的
assert(*sp == 100);
sp.reset();                 //停止shared_ptr的引用
assert(!sp卡塔尔;                //sp不再抱有此外指针(空指针)
return 0;
}
示例2:

//SFAutoPtr.h

从书中描述来看,就好像与拷贝结构函数、右值、类型调换有关。于是,结合auto_ptr的源代码,google之、baidu之,找了一推资料,终于起头搞清该难题。

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <cassert>

#pragma once

auto_ptr的具备权

using namespace boost;
using namespace std;

template<typename
T>

C++不足为道的智能指针有std::auto_ptr、boost::shared_ptr、boost::scoped_ptr、boost::shared_array、boost::scoped_array等。auto_ptr只是当中黄金年代种而已。可是,为啥auto_ptr才有auto_ptr_ref,而boost::shared_ptr却没有shared_ptr_ref呢?

class shared             //三个富有shared_ptr的类
{
private:
shared_ptr<int> p;
public:
shared(shared_ptr<int> p_) : p(p_卡塔尔国 { }  
//构造函数初阶化shared_ptr
void print()          //输出shared_ptr的援用计数和针对的值
{
cout << “count: ” << p.use_count()
<< ” v = ” << *p << endl;
}
};

class SFAutoPtr {

答案与auto_ptr的风味有关。auto_ptr重申对财富的拥有权(ownership)。也正是说,auto_ptr是”它所指对象”的具备者。而叁个指标只好归属多少个具有者,严禁一物二主,不然纵然重婚罪,意料外的意外之灾将光临。

void print_func(shared_ptr<int> p)
{
//一样输出shared_ptr的援用计数和针没错值
cout << “count: ” << p.use_count()
<< ” v = ” << *p << endl;
}

private:

为了保险auto_ptr的具有权唯生机勃勃,auto_ptr的正片构造函数和赋值操作符做了那样风流倜傥件事情:移除另二个auto_ptr的具有权。为了验证具有权的转移,请看上面包车型地铁代码示例:

int main()
{
shared_ptr<int> p(new int(110));
shared s1(p卡塔尔, s2(p卡塔尔(قطر‎;       //布局三个自定义的类

T* pointer; //**对象指针

#include <iostream>
#include <memory>
using namespace std;

s1.print();
s2.print();

size_t *ref_count; //**援引计数

int main(int argc, char **argv){
    auto_ptr<int> ptr1(new int(1));
    auto_ptr<int>
ptr2(ptr1卡塔尔;    //ptr1的具备权被转移到ptr2

*p = 20;           //修改shared_ptr所指的值
print_func(p);

void dec() { //**减少引用计数

    auto_ptr<int>
ptr3(NULL);
    ptr3 = ptr2;                 //ptr2的具有权被转移到ptr3

s1.print();

if(*ref_count
== 0) { //假设当前援引计数为0,则应当释放能源

    cout<<ptr1.get()<<endl;    
//结果为0
    cout<<ptr2.get()<<endl;    
//结果为0
    cout<<*ptr3<<endl;          //结果为1

return 0;
}
在表明了shared_ptr和五个shared实例后,指针被它们所分享,因而引用计数为3.printf_func函数内部拷贝了叁个shared_ptr对象,由此援用计数再扩充1,但当退出函数时拷贝自动析构,引用计数又过来3。

delete
pointer;

auto_ptr的正片布局函数与赋值操作符  

程序运营结果如下:

pointer =
nullptr;

由于要求实现具备权的调换,auto_ptr的正片构造函数和赋值操作符,与常常类的做法不太符合。大家得以看看MinGW
5.1.6落实的auto_ptr源代码:

 count:3 v = 110

delete
ref_count;

/**
*  @brief  An %auto_ptr can be constructed from another %auto_ptr.
*  @param  a  Another %auto_ptr of the same type.
*
*  This object now @e owns the object previously owned by @a a,
*  which has given up ownsership.
*/
auto_ptr(auto_ptr& __a) throw()
: _M_ptr(__a.release()) {}

count:3 v = 110

ref_count =
nullptr;

/**
*  @brief  %auto_ptr assignment operator.
*  @param  a  Another %auto_ptr of the same type.
*
*  This object now @e owns the object previously owned by @a a,
*  which has given up ownsership.  The object that this one @e
*  used to own and track has been deleted.
*/
auto_ptr&
operator=(auto_ptr& __a) throw () {
    reset(__a.release());
    return *this;
}

count:4 v = 20

return;

能够看看,auto_ptr的正片布局函数、赋值操作符,它们的参数都是auto_ptr&,而不是auto_ptr
const &。

count:3 v = 20

}

    平时的话,类的正片构造函数和赋值操作符的参数都以const
&。不过auto_ptr的做法也是客观的:管教具有权能够转移

–*ref_count;

    如果auto_ptr的正片布局函数和赋值操作符的参数是auto_ptr const
&,那么实参的具有权将不能够改造。因为改变具有权必要校正auto_ptr的积极分子变量,而实参确是五个const对象,不准更改。

}

右值与const &

void inc() { //**扩张引用计数

若是大家想写出下边的代码:

++*ref_count;

#include <memory>
using namespace std;

}

int main(int argc, char **argv) {
    auto_ptr<int>
ptr1(auto_ptr<int>(new int(1卡塔尔(قطر‎卡塔尔(قطر‎卡塔尔国;  //使用不常对象开展拷贝构造
    auto_ptr<int>
ptr2(NULL);
    ptr2 = (auto_ptr<int>(new int(2卡塔尔国卡塔尔卡塔尔;  //使用偶尔对象开展赋值
}

public:

     
若无定义auto_ptr_ref类及连锁的函数,那么这段代码将无法透过编写翻译。首要的缘故是,拷贝布局函数及赋值操作符的参数:auto_ptr<int>(new
int(1))和auto_ptr<int>(new
int(2))都是不常对象。有时对象归于标准的右值,而非const
&是不能够指向右值的(参见More Effective
C++,Item
19)。auto_ptr的正片布局函数及赋值操作符的参数类型偏巧是auto_ptr&,明显
非const &。

SFAutoPtr() : //**暗中同意构造函数,生成一个指南针

同理,下边包车型大巴两段代码,也不会通过编写翻译:

pointer(new T),

#include <iostream>
#include <memory>
using namespace std;
auto_ptr<int> f();
int main(int argc, char **argv) {
    auto_ptr<int>
ptr3(f(State of Qatar卡塔尔(قطر‎;  //使用有时对象开展拷贝布局
    auto_ptr<int>
ptr4(NULL);
    ptr4 = f(卡塔尔;          //使用不时对象开展赋值
}

ref_count(new size_t(0)) {}

#include <iostream>
#include <memory>
using namespace std;
auto_ptr<int> f(){
    return auto_ptr<int>(new int(3卡塔尔(قطر‎卡塔尔;  //这里实在也应用一时对象实行拷贝布局
}

 

习感到常类不会越过那几个主题素材,是因为她俩的正片布局函数及赋值操作符(不管是客商定义照旧编写翻译器生成的本子),参数都以const
&。

template<typename

Init_Type>

auto_ptr_ref之目的

SFAutoPtr(Init_Typeargs) : //**带参数的布局函数,对象满含指针

相传当年C++标准委员会的重重国家,因为那么些难题都想把auto_ptr从标准库中除去。幸而BillGibbons和GregColvin创造性地建议了auto_ptr_ref,息灭了那风流浪漫主题材料,世界沉静了。

pointer(new T(args…)),

auto_ptr_ref之原理

ref_count(new size_t(0)) {}

    很明朗,上面包车型客车构造函数,是能够选择auto_ptr不经常对象的。

 

auto_ptr(auto_ptr __a) throw() :
_M_ptr(__a.release()) { }

SFAutoPtr(const SFAutoPtr<T>& other) { //**拷贝构造函数,扩展引用计数

但另三个主题素材也很明显:上述布局函数不能够经过编写翻译。若是能经过编写翻译,就能沦为生生不息调用。

pointer =
other.pointer;

拷贝布局函数的参数类型必需是引用,

ref_count =
other.ref_count;

案由见链接:

inc();

我们稍作改良:

}

auto_ptr(auto_ptr_ref<element_type> __ref) throw()  //element_type就是auto_ptr的模版参数。
      : _M_ptr(__ref._M_ptr) { }

 

该版本的结构函数,能够接纳auto_ptr_ref的有的时候对象。假设auto_ptr能够隐式转形成auto_ptr_ref,那么大家就可见用auto_ptr有时对象来调用该结构函数。这些隐式转变轻便完结:

bool operator==(const SFAutoPtr<T>& other) const{ //相当于操作符,剖断指针是或不是等于,当时不该比较ref_count

template<typename _Tp1>
        operator
auto_ptr_ref<_Tp1>() throw()
        { return
auto_ptr_ref<_Tp1>(this->release()); }

return
pointer ==
other.pointer;

于今,我们可以写出上面包车型地铁代码,并得以经过编写翻译:

}

#include <memory>
using namespace std;

 

int main(int argc, char **argv) {
    auto_ptr<int>
ptr1(auto_ptr<int>(new int(1)));  //调用auto_ptr_ref版本的布局函数
}

const SFAutoPtr<T>& operator=(const SFAutoPtr<T>& other) { //赋值运算符,需求将眼下引述计数-1,赋值的援用计数**+1

同理,假诺大家再提供下边包车型客车函数:

if(this
== &other)

auto_ptr&
      operator=(auto_ptr_ref<element_type>
__ref) throw()
      {
    if (__ref._M_ptr != this->get())
      {
        delete _M_ptr;
        _M_ptr = __ref._M_ptr;
      }
    return *this;
      }

return *this;

那就是说,下边包车型地铁代码也得以透过编写翻译:

dec();

#include <iostream>
#include <memory>
using namespace std;

pointer =
other.pointer;

int main(int argc, char **argv) {
    auto_ptr<int>
ptr2(NULL);
    ptr2 = (auto_ptr<int>(new int(2)));  //调用auto_ptr_ref版本的赋值操作符
}

ref_count =
other.ref_count;

auto_ptr_ref之本质

inc();

本质上,auto_ptr_ref赋予了auto_ptr“援引”的语义,那或多或少方可从auto_ptr_ref的注明看出:

return *this;

/**
   *  A wrapper class to provide auto_ptr with reference semantics.
   *  For example, an auto_ptr can be assigned (or constructed from)
   *  the result of a function which returns an auto_ptr by value.
   *
   *  All the auto_ptr_ref stuff should happen behind the scenes.
   */
  template<typename _Tp1>
    struct auto_ptr_ref
    {
      _Tp1* _M_ptr;
      
      explicit
      auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }
    };

}

auto_ptr_ref之代码

 

此处列出auto_ptr_ref相关的函数,共参照他事他说加以考查:

T operator*(int) { //**解援引运算符

auto_ptr(auto_ptr_ref<element_type> __ref) throw()
: _M_ptr(__ref._M_ptr) {}

return *pointer;

auto_ptr&
operator=(auto_ptr_ref<element_type>
__ref) throw () {
    if (__ref._M_ptr != this->get()) {
        delete _M_ptr;
        _M_ptr = __ref._M_ptr;
    }
    return *this;
}

}

template<typename _Tp1>
operator
auto_ptr_ref<_Tp1>() throw
() {
    return
auto_ptr_ref<_Tp1> (this->release());
}

 

template<typename _Tp1>
operator auto_ptr<_Tp1>()
throw () {
    return auto_ptr<_Tp1>
(this->release());
}

operator T*() { //**指针运算符,适用于采用指针作为参数的函数

参照他事他说加以考查资料:

return
pointer;

auto_ptr之变迁

}

auto_ptr完结之我见

 

auto_ptr_ref的奇妙(上)

T* operator->() { //**成员访问操作符

auto_ptr_ref的奇妙(下)

return
pointer;

auto_ptr_ref
的目标是怎么样

}

关于auto_ptr_ref的一些难点

 

左值和右值

~SFAutoPtr() { //析构函数,需求将引用计数-1

注:

dec();

至于左值和右值

}

L = Location 可寻址
R = Read 可读

};

 

在乎此中的:

operator
T*() { //**指针运算符,适用于采用指针作为参数的函数

return
pointer;

}

此地就是为需求T*类别作为参数的函数提供接口,如下:

#include
<iostream>

using namespace std;

 

void func(int *){

    cout<<“hello
world”<<endl;

}

 

int main(){

    SFAutoPtr<int> t;

    func(t);

}

 

SFAutoPtr<int>能够很好地适应int*品类的参数,那为编码提供了无尽方便,但是随着也拉动了大器晚成部分安全祸患:

比方,有人或者写出上边那样的代码(没有错,正是小明!!!):

#include
<iostream>

using namespace std;

 

int *p;

 

void setP(){

    p=SFAutoPtr<int>(5);

}

 

int main(){

    setP();

    cout<<*p<<endl;

}

 

此间不会输出5,而是会输出一个随机值,以至前后相继崩溃,因为大家运用了对象管理财富,对象在销毁时,财富也会被放飞。招致那后生可畏情形的原由是AFAutoPtr<int>被隐式调换为了int*,制止这种调用的方式之大器晚成就是,不再提供隐式转变,确认保证编写出的每多少个调换都以技师想要的,使用get(State of Qatar而非operator
T*,将原达成中的operator
T*操作符替换为get(卡塔尔函数。有人可能要问,假诺程序猿非得写出:

#include
<iostream>

using namespace std;

 

int *p;

 

void setP(){

    SFAutoPtr<int> tmp(5);

    p=tmp.get();

}

 

int main(){

    setP();

    cout<<*p<<endl;

}

那样的代码怎么做?

对此作者不能不说:自作孽,不可活~~~

 

诚如接纳对象处理能源,都会提供接口访谈原来对象。是行使隐式访谈依然显式访谈,那至关心珍视要看其实须要,是为程序员带来便捷照旧给程序猿带给安全,你决定~~~