(P87+P88)boost智能指针:boost智能指针,scoped_ptr ,shared_ptr,weak_ptr,scoped_array/shared_array,pimpl技法

1.boost智能指针

  • 智能指针是利用RAII(Resource Acquisition Is Initialization:资源获取即初始化)来管理资源
    在构造函数中对资源初始化,在析构函数中对资源释放

  • 智能指针的本质思想是:
    (1)将堆对象的生存期用栈对象(智能指针)来管理,当new一个堆对象的时候,立刻用智能指针来接管。具体做法是:在构造函数进行初始化(用一个指针指向堆对象),在析构函数中调用delete来释放堆对象。
    (2)由于智能指针本身是一个栈对象,他的作用域结束的时候,会自动调用析构函数,从而调用了delete释放了堆对象

  • 常用的智能指针
    scoped_ptr对象既不能被拷贝,也不能被赋值
    intrusive_ptr不常用
    waek_ptr主要是解决循环引用的问题
    在这里插入图片描述

  • boost库在vs2008的配置,下载boost库后
    在这里插入图片描述

2.scoped_ptr

  • 智能指针本身是栈上对象
  • eg:P87\01.cpp
#include <boost/scoped_ptr.hpp>
#include <iostream>
using  namespace std;

class X
{
public:
    X()
    {
        cout <<  "X ..." << endl;
    }
    ~X()
    {
        cout <<  "~X ..." << endl;
    }
};

int main( void)
{
    cout <<  "Entering main ..." << endl;
    {
        //X堆对象,由智能指针栈对象pp来管理
        //智能指针栈对象pp销毁的时候,他所管理的堆对象也就跟着销毁了
        boost::scoped_ptr<X> pp( new X);

         //boost::scoped_ptr<X> p2(pp); //Error:所有权不能转移
    }
    cout <<  "Exiting main ..." << endl;

     return  0;
}
  • 测试:
    在这里插入图片描述

  • 断点:

        boost::scoped_ptr<X> pp( new X);

构造函数:px指针接管
在这里插入图片描述
析构函数:
在这里插入图片描述
typedef那两行是判定T是否是完整类型
在这里插入图片描述

 //boost::scoped_ptr<X> p2(pp); //Error:所有权不能转移

拷贝构造和=号运算符都是私有的
在这里插入图片描述

3.shared_ptr

  • 内部维护了一个引用计数reference
  • eg:P87\02.cpp
#include <boost/shared_ptr.hpp>
#include <iostream>
using  namespace std;

class X
{
public:
    X()
    {
        cout <<  "X ..." << endl;
    }
    ~X()
    {
        cout <<  "~X ..." << endl;
    }
};

int main( void)
{
    cout <<  "Entering main ..." << endl;
    boost::shared_ptr<X> p1( new X);
    cout << p1.use_count() << endl;//输出引用计数
    boost::shared_ptr<X> p2 = p1;//用p1对象初始化p2对象,调用拷贝构造函数,相当于共享一个对象
     //boost::shared_ptr<X> p3;
     //p3 = p1;

    cout << p2.use_count() << endl;//其值等于p1.use_count()
    p1.reset();//表示置空,显式的将引用计数-1,也可以不用,等程序结束的时候会进行的,因为是智能指针是栈对象嘛
    cout << p2.use_count() << endl;
    p2.reset();
    cout <<  "Exiting main ..." << endl;
     return  0;
}

  • 测试:
    在这里插入图片描述
  • 断点:
    boost::shared_ptr<X> p1( new X);

在这里插入图片描述
接着调用class X的构造函数,略;
接着调用shared_ptr的构造函数,px是一个指针,pn是管理引用计数的
在这里插入图片描述
在这里插入图片描述
要调用构造函数,对象成员的构造函数要先调用,会调用shared_count的构造函数
在这里插入图片描述
在这里插入图片描述
F11
在这里插入图片描述
F11,调用其构造函数
在这里插入图片描述
F11,
在这里插入图片描述
会调用基类的构造函数,强引用use_count_,弱引用weak_count_初始化为1
在这里插入图片描述

  • 断点:
    cout << p1.use_count() << endl;//输出引用计数

在这里插入图片描述
F11
在这里插入图片描述

  • 断点位置如下:直接在boost库中打断点
目的是看boost::shared_ptr<X> p2 = p1;调用默认拷贝构造函数后,引用计数怎么到这里++

引用计数+1的操作位置,这是原子性的自增操作,shared_ptr是线程安全的,内部的引用计数是线程安全的
在这里插入图片描述
shared_ptr没有提供拷贝构造函数,是编译器提供的默认拷贝构造函数
在这里插入图片描述
双击是看不到默认的拷贝构造函数
在这里插入图片描述
从上往下看,还看到还会调用shared_count的拷贝构造函数,因为shared_count是其内部所持有的对象
在这里插入图片描述
注意:shared_count const & r这里的r是一个常量,修改常量的值这里有个技巧:
pi_和r.pi指针指向同一个地方,现在堆pi_进行修改,对pi_所做的修改就是对r.pi所做的修改,这样就成功的将常量r进行了修改
在这里插入图片描述

const 常量
const int n = 100;
p = &n;
*p = 300;是可以修改常量的值的
  • eg:P87\03.cpp
#include <boost/shared_ptr.hpp>
#include <iostream>
using  namespace std;

class X
{
public:
    X()
    {
        cout <<  "X ..." << endl;
    }
    ~X()
    {
        cout <<  "~X ..." << endl;
    }
};

int main( void)
{
    cout <<  "Entering main ..." << endl;
    boost::shared_ptr<X> p1( new X);
    cout << p1.use_count() << endl;//输出引用计数
    boost::shared_ptr<X> p2 = p1;//用p1对象初始化p2对象,调用拷贝构造函数,相当于共享一个对象
     boost::shared_ptr<X> p3;
     p3 = p1;//等号运算符

    cout << p2.use_count() << endl;//其值等于p1.use_count()
    p1.reset();//表示置空,显式的将引用计数-1,也可以不用,等程序结束的时候会进行的,因为是智能指针是栈对象嘛
    cout << p2.use_count() << endl;
    p2.reset();
    cout <<  "Exiting main ..." << endl;
     return  0;
}

  • 测试:
    在这里插入图片描述
  • 断点:
     p3 = p1;

this_type会调用拷贝构造函数,并把它交换给this;即=运算符也是借助拷贝构造使得引用计数+1的;
在这里插入图片描述
在这里插入图片描述

  • auto_ptr不能放置在vector中,但是shared_ptr是可以放置在vector中的。
  • eg:P87\04.cpp
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <memory>
#include <vector>
using  namespace std;

class X
{
public:
    X()
    {
        cout <<  "X ..." << endl;
    }
    ~X()
    {
        cout <<  "~X ..." << endl;
    }
};

int main( void)
{
    //编译失败
    // vector<auto_ptr<X> > v;
    // auto_ptr<X> p(new X);
    // v.push_back(p);

    //boost::shared_ptr<X>是可以放到vector中的
    vector<boost::shared_ptr<X> > v;
    boost::shared_ptr<X> p(new X);
    v.push_back(p);//push_back内部会构造一个shared_ptr对象,与p一样,所以输出为2
    cout<<v.use_count<<endl;//2个对象都引用了X
    //当p对象销毁,向量v中的对象也被销毁的时候,引用计数减为0,则堆对象X自动被销毁

    

    return  0;
}

  • 测试:
    编译失败
    在这里插入图片描述
    push_back时会调用=号运算符
    在这里插入图片描述
    auto_ptr不能放置在vector中的原因是:
    auto_ptr的=号运算符如下:这里要求是非const的的引用,在做=运算时,_Right.release()所有权要发生转移,_Right变量的内容会发生改变
    在这里插入图片描述
    而vector里面的=号运算符,_Val是一个常量,是不能被更改的,即上面的_Right.release()是不能被更改的,不符合上面的auto_ptr<_Other>& _Right这里的接口,所以编译出错。
    在这里插入图片描述
    在这里插入图片描述
    编译成功的原因:接口是const的,符合接口。const变量不代表不能被更改,shared_ptr内部通过指针的方式来修改常量,但是接口保留const。
    所以,auto_ptr基本都不用了。
    在这里插入图片描述
  • shared_ptr注意事项
    详见:http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm
//实参传递到形参scoped_ptr<T>控制权不能转移,不能像下面这么用,根本就是错的
void f(shared_ptr<int>, int);
int g();

void ok()
{
	//这是没有内存泄漏的
	//f(p, g());会调用拷贝构造函数,形参与p共享对象,他俩都结束时,引用计数减为0
	//new int(2)对象会销毁
	shared_ptr<int> p(new int(2));
	f(p, g());
}

void bad()
{
	//可能存在内存泄漏
	//如果new int(2)构造,接着g()构造,此时在g()中抛出异常
	//没有调用shared_ptr的构造函数
	//那么new int(2)就没有被shared_ptr所接管,会造成内存泄漏
	//如果编译器的执行顺序是:从右往左,那么就不会,所以也要看编译器
	f(shared_ptr<int>(new int(2)), g())
}

4.weak_ptr

  • 循环引用问题
    在这里插入图片描述

  • eg:P87\05.cpp

#include  <boost/shared_ptr.hpp>
#include  <iostream>
using   namespace  std;

class  Parent;
class  Child;
typedef  boost::shared_ptr<Parent>  parent_ptr;
typedef  boost::shared_ptr<Child>  child_ptr;

class  Child
{
public:
        Child()
        {
                cout  <<   "Child  ..."  <<  endl;
        }
        ~Child()
        {
                cout  <<   "~Child  ..."  <<  endl;
        }
        parent_ptr  parent_;//持有一个Parent类的智能指针对象
};

class  Parent
{
public:
        Parent()
        {
                cout  <<   "Parent  ..."  <<  endl;
        }
        ~Parent()
        {
                cout  <<   "~Parent  ..."  <<  endl;
        }
        child_ptr  child_;//持有一个Child的智能指针对象
};

int  main( void)
{
        parent_ptr  parent( new  Parent);
        child_ptr  child( new  Child);

        //Parent父亲对象中的孩子指向孩子对象
        parent->child_  =  child;//孩子对象引用计数=2
        //Child孩子对象中的父亲指向父亲对象
        child->parent_  =  parent;//父亲对象引用计数=2

        //这样父亲对象和孩子对象都无法正常释放
         return   0;
}
  • 测试:析构函数都没调用
    在这里插入图片描述

  • 解决办法1:手动打破循环引用(不好,还不如用原生指针)

  • eg:P87\06.cpp

#include  <boost/shared_ptr.hpp>
#include  <iostream>
using   namespace  std;

class  Parent;
class  Child;
typedef  boost::shared_ptr<Parent>  parent_ptr;
typedef  boost::shared_ptr<Child>  child_ptr;

class  Child
{
public:
        Child()
        {
                cout  <<   "Child  ..."  <<  endl;
        }
        ~Child()
        {
                cout  <<   "~Child  ..."  <<  endl;
        }
        parent_ptr  parent_;//持有一个Parent类的智能指针对象
};

class  Parent
{
public:
        Parent()
        {
                cout  <<   "Parent  ..."  <<  endl;
        }
        ~Parent()
        {
                cout  <<   "~Parent  ..."  <<  endl;
        }
        child_ptr  child_;//持有一个Child的智能指针对象
};

int  main( void)
{
        parent_ptr  parent( new  Parent);
        child_ptr  child( new  Child);

        //Parent父亲对象中的孩子指向孩子对象
        parent->child_  =  child;//孩子对象引用计数=2
        //Child孩子对象中的父亲指向父亲对象
        child->parent_  =  parent;//父亲对象引用计数=2

        //这样父亲对象和孩子对象都无法正常释放
        parent->child_.reset();//让孩子对象的引用计数变成1,打破循环引用
        //child智能指针对象销毁时,当前引用计数从1->0,所以Child对象被销毁,且Child对象
        //又持有parent_,此时parent_引用计数减为1,当Parent对象销毁时,parent_计数从1->0,所以
        ///Parent会被销毁
         return   0;
}
  • 测试:
    在这里插入图片描述

  • 解决办法2:使用weak_ptr解决(自动非人工)
    在这里插入图片描述
    (1)强引用,只要有一个引用存在,对象就不能释放
    (2)弱引用,并不增加对象的引用计数(实际上是不增加use_count_, 会增加weak_count_);但它能知道对象是否存在
    如果存在,提升为shared_ptr(强引用)成功;
    如果不存在,提升失败;
    (3)通过weak_ptr访问对象的成员的时候,要提升为shared_ptr
    (4)shared_ptr是强引用
    weak_ptr是弱引用,即使对象销毁了,可能弱引用还存在,此时继续提升,可能会失败。

  • eg:

  • 测试:
    在这里插入图片描述

  • eg:P87\08.cpp

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>
#include <iostream>
using  namespace std;

class X
{
public:
    X()
    {
        cout <<  "X ..." << endl;
    }
    ~X()
    {
        cout <<  "~X ..." << endl;
    }

     void Fun()
    {
        cout <<  "Fun ..." << endl;
    }
};
int main( void)
{
    //weak_ptr通常要与shared_ptr配合使用
    boost::weak_ptr<X> p;//定义一个若指针对象引用
    {
        boost::shared_ptr<X> p2( new X);
        cout << p2.use_count() << endl;//=1
        p = p2;
        cout << p2.use_count() << endl;//p2是强引用,引用计数=1


    //要访问X中的数据成员,首先要将弱引用p提升为shared_ptr
    //弱引用没有重载->指针运算符,强引用有的
        boost::shared_ptr<X> p3 = p.lock();
        if (!p3)
            cout<<"object is destroyed"<<endl;//p.lock()看下能否提升,lock()表示提升,也表示锁定对象,防止被销毁
        else
            p3->Fun();
    }
    //p2引用的对象会被销毁,因为引用计数=1

     boost::shared_ptr<X> p4 = p.lock();
    if (!p4)
        cout<<"object is destroyed"<<endl;
    else
        p4->Fun();

     return  0;
}
  • 测试:
    在这里插入图片描述

5.scoped_array/shared_array

  • 针对于数组
  • eg:P87\10.cpp
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>
#include <iostream>
using  namespace std;

class X
{
public:
    X()
    {
        cout <<  "X ..." << endl;
    }
    ~X()
    {
        cout <<  "~X ..." << endl;
    }

     void Fun()
    {
        cout <<  "Fun ..." << endl;
    }
};
int main( void)
{
    //weak_ptr通常要与shared_ptr配合使用
    boost::weak_ptr<X> p;//定义一个若指针对象引用
    {
        boost::shared_ptr<X> p2( new X);
        cout << p2.use_count() << endl;//=1
        p = p2;
        cout << p2.use_count() << endl;//p2是强引用,引用计数=1


    //要访问X中的数据成员,首先要将弱引用p提升为shared_ptr
    //弱引用没有重载->指针运算符,强引用有的
        boost::shared_ptr<X> p3 = p.lock();
        if (!p3)
            cout<<"object is destroyed"<<endl;//p.lock()看下能否提升,lock()表示提升,也表示锁定对象,防止被销毁
        else
            p3->Fun();
    }
    //p2引用的对象会被销毁,因为引用计数=1

     boost::shared_ptr<X> p4 = p.lock();
    if (!p4)
        cout<<"object is destroyed"<<endl;
    else
        p4->Fun();

    boost::scoped_array<X> xx(new X[3]);

    // boost::scoped_ptr<X> xx(new X[3]);//error,析构不带[]
    
     return  0;
}
  • 测试:
    在这里插入图片描述
    析构函数带[]
    在这里插入图片描述
    在这里插入图片描述

6.PIMPL技法

  • 引入更多的头文件,降低编译速度
    y.cpp和main.cpp都引入了x.h
  • 提高模块的耦合度
    编译期:Y类依赖X的实现,X类改变了,Y需要重新编译
    运行期:若X类有子类,子类若有Fun(),则无法实现替换,无法使用多态
  • 降低了接口的稳定程度
    (1)对于库的使用,方法不能改变
    (2)对于库的编译,动态库的变更,客户程序不用重新编译
    若没有下面的动态库和客户程序的区分,那么如果X改变,则会影响Y,就会影响main.cpp,则所有程序都需要重新编译
  • 不使用PIMPL的eg

//假设下面是动态库start
//file y.h
#include "x.h"
class Y
{
public:
    void Func_Y();
    X x_;
};

//file y.cpp
#include "y.h"
void Y::Fun()
{
    return x_.Fun_X();
}
//end

//假设下面是客户程序
//file main.cpp
#include "y.h"
int main(void)
{
    Y y;
    y.Fun();
}
  • PIMPL技法
  • PIMPL(private implementation或pointer to implementation)也称之为handle/body idiom
    将实现隐藏,或者将指针指向一个实现,或者指针称之为handle,指针指向的实现称之为body(指针指向惯用法)
  • PIML背后的思想是把客户与所有关于类的私有部分隔离开。避免其他类知道其内部结构
    具体做法就是用一个指针来解决,因为类X发生了改变,指针大小是不变的,所以Y类是不用编译的,可以降低编译量
  • 降低编译依赖,提高重编译速度
  • 接口和实现分离
  • 降低模块的耦合度
    编译器
    运行期
  • 提高了接口的稳定程度
    (1)对于库的使用,方法不能改变
    (2)对于库的编译,动态库的变更,客户程序不用编译
    假设X发生改变,意味着库要进行变更,但是客户程序不需要编译,因为Y与其内部的实现细节是分离的,只要Y的接口保持不变。
  • PIMPL的eg:P87\12.cpp

//假设下面是动态库start
//y.h不需要包含x.h,只需要前向声明,因为指针的原因X*
//file y.h
class Y
{
    Y();
    ~Y();
    void Fun();
    X* px_;//即使类X发生变化,Y类不需要编译,指针所指向的类到底是怎么实现的,分离了
            //因为指针总是4个字节或者8个字节
            //在运行期,还可以应用多态,因为这是指针
            //也可以用智能指针,因为智能指针所持有的对象类型发生了改变,但智能指针的大小是不会改变的
            //智能指针还能自动管理px_的生存期
};

//file y.cpp
#include "x.h"
Y::Y() : px_(new X) {}
Y::~Y() {delete px_; px_ = 0;}
void Y::Fun() {return px_->Fun();}
//end

//假设下面是客户程序
//file main.cpp
#include "y.h"//这里y.h没有包含x.h
int main(void) 
{
    Y y;
    y.Fun();
}
  • eg:pimpl的又一个eg
//类Y依赖下面的三个类,相当于要依赖他们具体的实现了
class Y
{
	A a;
	B b;
	C c;
};

可以将他们提升到一个类中
class Y
{
	Impl* p;
};
class Impl
{
	A a;
	B b;
	C c;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值