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

news/2025/2/15 5:52:25/

文章目录

    • 1.boost智能指针
    • 2.scoped_ptr
    • 3.shared_ptr
    • 4.weak_ptr
    • 5.scoped_array/shared_array
    • 6.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一样,所以输出为2cout<<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;//=1p = 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()表示提升,也表示锁定对象,防止被销毁elsep3->Fun();}//p2引用的对象会被销毁,因为引用计数=1boost::shared_ptr<X> p4 = p.lock();if (!p4)cout<<"object is destroyed"<<endl;elsep4->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;//=1p = 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()表示提升,也表示锁定对象,防止被销毁elsep3->Fun();}//p2引用的对象会被销毁,因为引用计数=1boost::shared_ptr<X> p4 = p.lock();if (!p4)cout<<"object is destroyed"<<endl;elsep4->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;
};
  • 参考:从零开始学C++之boost库(一):详解 boost 库智能指针(scoped_ptr 、shared_ptr 、weak_ptr 源码分析)

http://www.ppmy.cn/news/289765.html

相关文章

数据库p88-表格代码mysql

#81-例5.4 use stuexpm; insert into studentInfo values (181001,成志强,男,1998-08-17,计算机,北京市海淀区), (181002,孙红梅,女,1997-11-23,计算机,成都市锦江区), (181003,朱丽,女,1998-02-19,计算机,北京市海淀区), (184001,王智勇,男,1997-12-05,电子信息工程,null), (1…

《汇编语言》王爽 P88 实验3

《汇编语言》王爽 P88 实验3 Mac系统配置这些蛮麻烦的&#xff1a; 下载个DOSBOX&#xff0c;再下载debug&#xff0c;在DOSBOX中把debug挂到C盘去吧 比如我想把air下面的debug文件挂成C盘&#xff0c;就括号中这条命令&#xff1a; &#xff08;mount c /Users/air/debug&…

IRIG-B码对时

前言&#xff1a;最近我在学习IRIG-B码对时时一直对这个对时转换存在一些疑问&#xff0c;几乎在网上搜遍了资料也没有一个清晰的解答&#xff0c;最终直接使用一台时钟服务器、一台精准时间测试仪&#xff0c;进行IRIG-B测试&#xff0c;查看编码译码内容才对IRIG-B对时解析有…

p88-高级-消息- RabbitTemplate发送接受消息序列化机制

SmarTTY中首先搭建环境&#xff0c;过程如图所述&#xff0c;每次只需要走这几步&#xff0c;就可以在rabbitmq的客户端访问到&#xff1a; RabbitTemplate发送接受消息&序列化机制 1、pom.xml <?xml version"1.0" encoding"UTF-8"?> <p…

UML类图箭头总结

概念 1.类&#xff08;Class&#xff09;&#xff1a;使用三层矩形框表示 . 第一层显示类的名称&#xff0c;如果是抽象类&#xff0c;则就用斜体显示 . 第二层是字段和属性 . 第三层是类的方法 . 注意 : 前面的符号&#xff0c;‘’表示public&#xff0c;‘-’表示private&am…

九宫格多级OLED(LCD)菜单

目录 函数指针 函数指针的定义方式 使用举例 菜单索引 索引格式 索引列表 按键获取 UI刷新 光标 页面函数 OLED菜单实现的方法有很多&#xff0c;本文介绍的方法也很常见。相对来说比较特殊的地方就是九宫格&#xff0c;但实现起来难度也不是特别大。本文只介绍了该菜…

PEP8

代码布局 缩进 函数参数列表if 语句条件集合 制表符 or 空格行的最大长度二元运算符空行源文件编码Import 导入模块级的魔法名称 字符串引号表达式和语句中的空格 避免使用无关的空格其他建议 注释 块注释行内注释文档字符串 命名规范 命名风格命名约定 应避免的名字包名和模块…

C++核心之程序的内存模型P84-P88

内存分区模型&#xff1a; C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的全局区&#xff1a;存放全局变量和静态变量以及常量栈区&#xff1a;由编译器自动分配释放, 存放函数的参数值,局部…