当前位置: 首页 > news >正文

C++(智能指针)

智能指针

1.基础:

1.1 概念

智能指针是用于自动管理动态分配内存的RAII(Resource Acquisition Is Initialization)对象。它们通过自动释放内存来防止内存泄漏,是替代裸指针的安全工具。

1.2 解析

众所周知,堆内存对象 需要 手动 使用 delete 销毁 如果 没有 使用 delete 销毁 就会 造成 内存 泄漏
所以C + + 在ISO9 8 标准 引入了 智能 指针 概念 I S O 1 1 趋于 完善

智能指针可以堆内存对象具有栈内存对象特点原理需要手动回收内内存对象套上一个栈内存的模板类对象即可

如下图,智能指针类似把堆区指针对象放到新的指针类中进行管理,这样就可以和栈区对象一样,所处的{}结束就会自动释放内存,不用手动delete。

2.分类:

自动指针:auto_ptr        (C++ISO98,已被废除

唯一指针:unique_ptr    (C++IOS11)

共享指针:shared_ptr    (C++IOS11)

虚指针:weak_ptr          (C++IOS11)

2.1 自动指针

(1)使用:
#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":构造函数" << endl;}~Animal(){cout << breed << ":析构函数" << endl;}void print(){cout << "物种:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){{Animal *a = new Animal("狗","mike");auto_ptr<Animal> ap1(a);                //自动指针管理堆区指针对象aap1.get()->print();                     //使用get函数调用管理的指针对象//ap1.release();                        //直接放弃对已有指针对象a的管理,不会释放堆区空间,如果不delete对象a可能导致内存泄露//ap1.reset();                          //放弃对已有指针对象a的管理,同时释放堆区空间/**1. 放弃对原有指针的管理,同时释放空间,*2. 再创建新的堆区对象*3. ap1指针对新的堆区指针对象进行管理*/ap1.reset(new Animal("猫","lisa"));ap1.get()->print();                     //打印新管理的对象的成员变量cout << "程序块运行结束" << endl;}cout << "程序运行结束" << endl;return 0;
}

 

(2)问题(被废除原因):

由于成员变量存在指针类型因此拷贝构造函数赋值运算符重载使用出现问题浅拷贝不同auto_ptr复制语义会造成资源控制权转移问题

#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":构造函数" << endl;}~Animal(){cout << breed << ":析构函数" << endl;}void print(){cout << "物种:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){{Animal *a = new Animal("狗","mike");auto_ptr<Animal> ap1(a);                //自动指针管理堆区指针对象aauto_ptr<Animal> ap2(ap1);              //显示调用拷贝函数cout << ap1.get() << " " << ap2.get() << endl;auto_ptr<Animal> ap3 = ap2;             //隐式调用拷贝函数cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << endl;auto_ptr<Animal> ap4;ap4 = ap3;                              //调用的是重载的赋值运算符cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << " " << ap4.get() << endl;cout << "程序块运行结束" << endl;}cout << "程序运行结束" << endl;return 0;
}

2.2 唯一指针

(1)特点:

1.和auto_ptr基本一致,唯一的不同就是unique_ptr指针对资源对象有唯一控制权,不能使用常规语法直接赋值和调用拷贝构造函数拷贝资源对象的控制权。

2.如果想让别的unique_ptr指针抢夺资源控制权就使用move(ap1)。

(2)使用:
#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":构造函数" << endl;}~Animal(){cout << breed << ":析构函数" << endl;}void print(){cout << "物种:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){{Animal *a = new Animal("狗","mike");unique_ptr<Animal> up1(a);                //自动指针管理堆区指针对象aup1.get()->print();//unique_ptr<Animal> up2(up1);                  //不使用move不能转移控制权,下面两种方式一样unique_ptr<Animal> up2(move(up1));              //显示调用拷贝函数cout << up1.get() << " " << up2.get() << endl;unique_ptr<Animal> up3 = move(up2);             //隐式调用拷贝函数cout << up1.get() << " " << up2.get() << " " << up3.get() << endl;unique_ptr<Animal> up4;up4 = move(up3);                              //调用的是重载的赋值运算符cout << up1.get() << " " << up2.get() << " " << up3.get() << " " << up4.get() << endl;cout << "程序块运行结束" << endl;}cout << "程序运行结束" << endl;return 0;
}

2.3 共享指针

(1)特点:

1.对一个堆区对象进行管理,解决了抢夺控制权转换的问题,可以多个共享指针一起管理一个堆区对象。

2.新增一个引用计数,对一起管理同一个对象的指针计数,每多一个共享指针管理这个堆区对象,引用计数加一。

3.当管理这个堆区空间的所有共享指针都被释放,才会回收这个堆区对象。 

4.shared_ptr有两种创建方式,一种和之前的创建方式一样。第二种常用,是其他智能指针没有的,使用:shared_ptr<类型> sp1 = make_shared<类型>(初始化内容)。

(2)使用:
#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":构造函数" << endl;}~Animal(){cout << breed << ":析构函数" << endl;}void print(){cout << "物种:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){shared_ptr<Animal> sp3;{//shared_ptr<Animal> sp1(new Animal("狗","mike"));              //先创建堆区对象,再让智能 指针引用管理堆区对象,速度慢shared_ptr<Animal> sp1 = make_shared<Animal>("狗","mike");       //直接创建智能指针并且引用对象,一步到位sp1.get()->print();cout << sp1.get() << endl;cout << "引用计数:" << sp1.use_count() << endl;shared_ptr<Animal> sp2 = sp1;               //隐式调用拷贝构造函数cout << sp1.get() << " " << sp2.get() << endl;cout << "引用计数:" << sp1.use_count() << endl;sp3 = sp2;              //赋值运算符重载cout << sp1.get() << " " << sp2.get() << " " << sp3.get() << endl;cout << "引用计数:" << sp1.use_count() << endl;cout << "程序块运行结束" << endl;}cout << "引用计数:" << sp3.use_count() << endl;         //程序块结束,只剩下ap3管理对象cout << "程序运行结束" << endl;return 0;
}

2.4 虚指针

虚指针weak_ptr是用来观查共享指针所管理的资源,像一个旁观者,来记录观查的对象(堆区对象)是否被shared_ptr管理,有几个share_ptr在管理。

(1)特点:

1.虚指针weak_ptr是一个不控制资源对象管理的智能指针,不会影响资源的引用计数,主要的目的是协助share_ptr工作

2.通过weak_ptr的构造函数,参数传入一个持有资源对象的share_ptr或者weak_ptr的指针

3.weak_ptr与资源呈弱相关性,不能单独对资源进行控制,不可以调用get()函数操作资源

4.weak_ptr可以调用.lock函数让shared_ptr指针获得一个持有资源的share_ptr,在调用.lock函数前要先检测weak_ptr的引用计数是否大于零(观察的资源是否还有shared_ptr指针还在管理,如果没有,对应的资源也不存在)。

#include <iostream>
#include <memory>using namespace std;class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":构造函数" << endl;}~Animal(){cout << breed << ":析构函数" << endl;}void print(){cout << "物种:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){weak_ptr<Animal> wp2;shared_ptr<Animal> sp3;{shared_ptr<Animal> sp1 = make_shared<Animal>("狗","mike");       //直接创建智能指针并且引用对象,一步到位sp1.get()->print();                            //打印weak_ptr<Animal> wp1(sp1);                  //使用虚函数wp1来观测共享指针对堆区对象引用次数,不会增加引用计数cout << "引用计数:" << sp1.use_count() << endl;cout << "引用计数:" << wp1.use_count() << endl;shared_ptr<Animal> sp2 = sp1;               //隐式调用拷贝构造函数//sp1.reset();                              //删除当前共享指针,如果还有其他共享指针管理就不释放所管理的资源wp2 = sp2;                                  //wp2也观测堆区对象cout << "引用计数:" << sp1.use_count() << " " << wp2.use_count() << endl;sp3 = sp2;cout << "程序块运行结束" << endl;}//即使观测的共享指针被释放,wp2依旧存在,wp1被释放cout << "引用计数:" << wp2.use_count() << endl;         //程序块结束,虚函数观测管理资源对象的引用计数--0cout << "程序运行结束" << endl;return 0;
}

3.手写一个共享指针Share_ptr类

  • 构造函数
  • 拷贝构造函数
  • 赋值运算符
  • get函数
  • use_count函数
  • reset函数
  • 析构函数

代码实现:

#include <iostream>
#include <memory>using namespace std;template <class T>
class Shareptr{
private:T *res = nullptr;int *count = nullptr;
public:Shareptr(){}Shareptr(T *s):res(s),count(new int(1)){}       //构造函数Shareptr(const Shareptr &sp):res(sp.res),count(sp.count){ //拷贝构造函数(*count)++;}T* get()const{                      //调用管理的资源return res;}int use_count()const{return *count;}void reset(){                   //删除当前共享指针,如果还有其他共享指针管理资源对象,不删除资源对象if(count != nullptr && res != nullptr){       //如果当前共享指针不是空(*count)--;if((*count) == 0){delete res;delete count;}res = nullptr;count = nullptr;}}Shareptr &operator =(const Shareptr &sp){       //赋值运算符重载if(&sp != this){reset();res = sp.res;count = sp.count;(*count)++;}return *this;}~Shareptr(){reset();}
};class Animal{
private:string breed;string name;
public:Animal(string breed,string name):breed(breed),name(name){cout << this->breed << ":构造函数" << endl;}~Animal(){cout << breed << ":析构函数" << endl;}void print(){cout << "物种:" << breed << endl;cout << "名字:" << name << endl;}
};int main (){Shareptr<Animal> sp3;{//Shareptr<Animal> sp1 = make_shared<Animal>("金渐层","lisa");//Shareptr<Animal> sp1 = new Animal("金渐层","lisa");      //隐式调用构造函数Shareptr<Animal> sp1(new Animal("金渐层","lisa"));       //显示调用构造函数sp1.get()->print();cout << sp1.use_count() << " " << sp1.get() << endl;Shareptr<Animal> sp2 = sp1;                              //调用拷贝构造函数cout << sp1.use_count() << " " << sp1.get() << endl;cout << sp2.use_count() << " " << sp2.get() << endl;sp3 = sp1;                                               //调用重载的赋值运算符cout << sp1.use_count() << " " << sp1.get() << endl;cout << sp2.use_count() << " " << sp2.get() << endl;cout << sp3.use_count() << " " << sp3.get() << endl;}cout << "程序结束" << endl;return 0;
}

http://www.lryc.cn/news/575113.html

相关文章:

  • LeetCode 3298.统计重新排列后包含另一个字符串的子字符串数目2
  • ivx创建一个测试小案例
  • Vue3插槽
  • 基于springboot+vue的智慧农业专家远程指导系统
  • 批量DWG转PDF工具
  • ES和 Kafka 集群搭建过程中的典型问题、配置规范及最佳实践
  • RK平台HDMI-IN/camera调试:预留CMA内存
  • Mac安装Apache CXF的时候报错:/Library/Internet: No such file or directory
  • 打造属于你的AI智能体,从数据开始 —— 使用 Bright Data MCP+Trae快速构建垂直智能体
  • 操作系统之内存管理(王道)
  • Azure 自动化:所需状态配置 (DSC)
  • UniApp 开发第一个项目
  • Python虚拟环境管理:conda、venv、pipenv三国杀
  • JSON框架转化isSuccess()为sucess字段
  • Fisco Bcos学习 - 开发第一个区块链应用
  • PAC 学习框架:机器学习的可靠性工程
  • 通俗易懂解读BPE分词算法实现
  • 回归预测 | Matlab实现KAN神经网络多输入单输出回归预测模型
  • 轻巧灵动,智启未来 ——Kinova Gen3 Lite 机器人轻松解锁各行业自动化新姿势
  • 领域驱动设计(DDD)【13】之重构中的坏味道:深入理解依恋特性(Feature Envy)与表意接口模式
  • 香港电讯携手Palo Alto Networks,护航企业跨区域数字化之旅
  • 第8章项目进度管理归纳总结补充
  • 英飞凌高性能BMS解决方案助力汽车电动化
  • python学智能算法(十六)|机器学习支持向量机简单示例
  • 基于MATLAB图像特征识别及提取实现图像分类
  • spring event(spring事件)
  • 从 HLS 到 Verilog 的转变解析1:以 AXI 接口为例
  • 云原生灰度方案对比:服务网格灰度(Istio ) 与 K8s Ingress 灰度(Nginx Ingress )
  • jenkins 越用越卡,打开网页缓慢
  • CLion 调试时 Command Timed Out 问题解决方案