智能指针并非指针,他是一个类对象。这个对象托管了堆区的资源。

当栈上的智能指针对象出栈时,所托管的堆区对象自动进行析构。
用到的知识点就是栈上对象出栈自动销毁的特性,因为出栈即自动调用析构。
RAII 是 resource acquisition is initialization 的缩写,意为“资源获取即初始化”。它是 C++ 之父 Bjarne Stroustrup 提出的设计理念,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。在 RAII 的指导下,C++ 把底层的资源管理问题提升到了对象生命周期管理的更高层次。
那么到底什么是 RALL 机制?
使用 C++ 时,最让人头疼的便是内存管理,但却又正是对内存高度的可操作性给了 C++ 程序猿极大的自由与装逼资本。
当我们 new 出一块内存空间,在使用完之后,如果不使用 delete 来释放这块资源则将导致内存泄露,这在中大型项目中是极具破坏性的。但是人无完人,我们并不能保证每次都记得释放无法再次获取到且不再使用的内存。
- #include
- #include
-
- using namespace std;
-
- class Stu
- {
- int age;
- string name;
- public:
- Stu(string name,int age)
- {
- this->age=age;
- this->name=name;
-
- cout<<"STU的构造"<
- }
-
- ~Stu()
- {
- cout<<"STU的析构"<
- }
-
- void show_info()
- {
- cout<<this->age<<this->name<
- }
-
- };
-
- int main()
- {
- auto_ptr
stu(new Stu("lisi",12)) ; - stu->show_info();
- return 0;
- }
结果图:

分析:像这样我们即使不手动释放堆区的资源,他也会自动释放。
2.2auto_ptr的底层实现:
- #include
-
- using namespace std;
- class Stu
- {
- string name;
- int age;
- public:
- Stu(string name,int age)
- {
- this->name=name;
- this->age=age;
- cout<<"stu的构造"<
- }
-
- ~Stu()
- {
- cout<<"stu的析构"<
- }
-
- void show_info()
- {
- cout<<this->name<<","<<this->age<
- }
- };
-
- template <class T>
- class Auto_ptr
- {
- T* ptr;
- public:
- Auto_ptr(T* _ptr)
- {
- this->ptr=_ptr;
- }
-
- ~Auto_ptr()
- {
- delete ptr;
- ptr=NULL;
- }
-
- Auto_ptr(const Auto_ptr& other)
- {
- this->ptr=other.ptr;
- const_cast
(other)->ptr=nullptr; - }
-
- Auto_ptr& operator=(const Auto_ptr& other)
- {
- if(this->ptr!=nullptr){
- delete this->ptr;
- this->ptr=nullptr;
- }
-
- this->ptr=other.ptr;
- }
-
- T* get()
- {
- return this->ptr;
- }
-
- T* operator->()
- {
- return this->ptr;
- }
- };
-
- int main()
- {
-
- Auto_ptr
stu(new Stu("lisi",20)) ; - stu.operator->()->show_info();
- stu->show_info();
-
- return 0;
- }
结果图:

分析:这就是智能指针auto_ptr的实现。
2.3auto_ptr的缺点:
2.3.1通过代码来说明问题:
- #include
- #include
-
- using namespace std;
- class Stu
- {
- string name;
- int age;
- public:
- Stu(string name,int age)
- {
- this->name=name;
- this->age=age;
- cout<<"stu的构造"<
- }
-
- ~Stu()
- {
- cout<<"stu的析构"<
- }
-
- void show_info()
- {
- cout<<this->name<<","<<this->age<
- }
- };
-
- template <class T>
- class Auto_ptr
- {
- T* ptr;
- public:
- Auto_ptr(T* _ptr)
- {
- this->ptr=_ptr;
- cout<<"构造"<
- }
-
- ~Auto_ptr()
- {
- delete ptr;
- ptr=NULL;
- cout<<"析构"<
- }
-
- Auto_ptr(const Auto_ptr& other)
- {
- this->ptr=other.ptr;
- const_cast
(other)->ptr=nullptr; - }
-
- Auto_ptr& operator=(const Auto_ptr& other)
- {
- if(this==&other){
- return *this;
- }
-
- if(this->ptr!=nullptr){
- delete this->ptr;
- this->ptr=nullptr;
- }
-
- this->ptr=other.ptr;
- }
-
- T* get()
- {
- return this->ptr;
- }
-
- T* operator->()
- {
- return this->ptr;
- }
- };
-
- int main()
- {
-
-
- Auto_ptr
stu(new Stu("zhangsan",20)) ; - stu->show_info();
- Auto_ptr
stu1=stu; - stu1->show_info();
-
- return 0;
- }
报错图:

分析:当我们在main函数里面这么写的时候就会出现问题,就是我只要以把数值赋给另外一个,原来的那个就会消失,要是再大型项目中,这肯定会导致很多小问题,而且我们从这个报错也看到了,系统并不让我们删除,也说明了这个问题的严重性,所以这个智能指针被废弃了,当然也还能用哈。
3.带有引用记数器的共享智能指针shared_ptr(重点)
3.1shared_ptr的使用方法:
- #include
- #include
-
- using namespace std;
- class Stu
- {
- string name;
- int age;
- public:
- Stu(string name,int age)
- {
- this->name=name;
- this->age=age;
- cout<<"stu的构造"<
- }
-
- ~Stu()
- {
- cout<<"stu的析构"<
- }
-
- void show_info()
- {
- cout<<this->name<<","<<this->age<
- }
- };
-
-
-
- int main()
- {
-
- shared_ptr
stu(new Stu("lisi",23)) ; - stu->show_info();
- cout<
use_count()< -
- shared_ptr
stu1=stu; - stu1->show_info();
- cout<
use_count()< -
- return 0;
- }
结果图:

3.2shared_ptr的底层实现:
- #include
-
- using namespace std;
-
- using namespace std;
- class Stu
- {
- string name;
- int age;
- public:
- Stu(string name,int age)
- {
- this->name=name;
- this->age=age;
- cout<<"stu的构造"<
- }
-
- ~Stu()
- {
- cout<<"stu的析构"<
- }
-
- void show_info()
- {
- cout<<this->name<<","<<this->age<
- }
- };
- template <class T>
- class Recounts
- {
- T* ptr;
- int counts;
- public:
- Recounts(T* _ptr=nullptr)
- {
- this->ptr=_ptr;
-
- if(this->ptr!=nullptr){
- this->counts=1;
- }
- }
-
- void my_add()
- {
- ++(this->counts);
- }
-
- int my_sub()
- {
- return --(this->counts);
- }
-
- int get_counts()
- {
- return this->counts;
- }
- };
-
- template <class T>
- class Shared_ptr
- {
- Recounts
* recounts; - T* ptr;
- public:
- Shared_ptr(T* _ptr=nullptr)
- {
- this->ptr=_ptr;
- if(this->ptr!=nullptr){
- recounts=new Recounts
(ptr); - }
- }
-
- ~Shared_ptr()
- {
- if(recounts->my_sub()==0){
- delete this->ptr;
- delete recounts;
- this->ptr=nullptr;
- recounts=nullptr;
- }
- }
-
- Shared_ptr(const Shared_ptr& other)
- {
- this->ptr=other.ptr;
- this->recounts=other.recounts;
-
- recounts->my_add();
- }
-
- Shared_ptr& operator=(const Shared_ptr& other)
- {
- if(this==&other){
- return *this;
- }
-
- if(this->ptr!=nullptr){
- delete this->ptr;
- this->ptr=nullptr;
- }
-
- this->ptr=other.ptr;
- this->recounts=other.recounts;
- recounts->my_add();
- }
-
- T* operator->()
- {
- return this->ptr;
- }
-
- int usr_counts()
- {
- return recounts->get_counts();
- }
- };
-
-
-
- int main()
- {
- Shared_ptr
stu(new Stu("lisi",20)) ; - stu->show_info();
- stu.usr_counts();
-
- Shared_ptr
stu1=stu; - stu1->show_info();
- stu1.usr_counts();
-
- return 0;
- }
结果图:

4.weak_ptr
4.1概念:
和shared_ptr搭配着用,防止内存泄漏。
4.1代码说明:
4.2.1shared_ptr的缺点:
- #include
- #include
-
- using namespace std;
-
- class B;
- class A
- {
- public:
- shared_ptr ptr_b;
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
-
- void show_info()
- {
- cout<<"mrr ai chi shi"<
- }
-
- };
-
- class B
- {
- public:
- shared_ptr ptr_a;
- B()
- {
- cout<<"B的构造"<
- }
-
- ~B()
- {
- cout<<"B的析构"<
- }
-
- void single()
- {
- ptr_a->show_info();
- }
-
- };
-
- int main()
- {
- shared_ptr pa(new A);
- shared_ptr pb(new B);
-
- pa->ptr_b=pb;
- pb->ptr_a=pa;
-
- pb->single();
-
- return 0;
- }
结果图:

由结果图片我们可以看出来,没有进行析构,这是由于当给pa给与堆区地址的时候,计数器的数字加1,当又进行拷贝赋值的时候,pa->ptr_b又在pa的基础上又加了1,所以pa->ptr->b的计数器的数字为2,又因为他们都共享一块内存pb的计数器数字也为2。
具体实现图:

这个时候我们就可以引出来我们的weak_ptr和shared_ptr是天然的搭档。
4.2weak_ptr和shared_ptr的天然搭配代码:
- #include
- #include
-
- using namespace std;
-
- class B;
- class A
- {
- public:
- weak_ptr ptr_b;
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
-
- void show_info()
- {
- cout<<"mrr ai chi shi"<
- }
-
- };
-
- class B
- {
- public:
- weak_ptr ptr_a;
- B()
- {
- cout<<"B的构造"<
- }
-
- ~B()
- {
- cout<<"B的析构"<
- }
-
- void single()
- {
- if(ptr_a.lock()==nullptr){
- return;
- }
-
- ptr_a.lock()->show_info();
- }
-
- };
-
- int main()
- {
- shared_ptr pa(new A);
- shared_ptr pb(new B);
-
- pa->ptr_b=pb;
- pb->ptr_a=pa;
-
- pb->single();
-
- return 0;
- }
结果图:

分析:因为使用weak_ptr后计数器不会加1, 这样就能防止内存泄漏了。
5.独占智能指针:unique_ptr
5.1概念:
独一无二的,唯一的智能指针,他是不共享的,他不想其它的共享对象也操作他的指向的堆区空间,他是唯一一个可操作这个堆区空间的对象。也就是说他的类对象是不可以发生拷贝构造与赋值运算符重载的。
5.2使用方式:
- #include
- #include
-
- using namespace std;
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
-
- void show_info()
- {
- cout<<"mrr ai chi shi"<
- }
-
- };
-
-
-
- int main()
- {
- unique_ptr a(new A);
- a->show_info();
-
- return 0;
- }
结果图:

这个智能指针修饰的内存只能一个人享用,如图:

5.3底层实现:
- #include
- #include
-
- using namespace std;
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
-
- void show_info()
- {
- cout<<"mrr ai chi shi"<
- }
-
- };
- template <class T>
- class Unique_ptr
- {
- T* ptr;
- public:
- Unique_ptr(T* _ptr)
- {
- this->ptr=_ptr;
- }
-
- Unique_ptr(const Unique_ptr& other)=delete ;
- Unique_ptr& operator=(const Unique_ptr& other)=delete;
-
- ~Unique_ptr()
- {
- delete this->ptr;
- this->ptr=nullptr;
- }
-
- T* operator->()
- {
- return this->ptr;
- }
- };
-
-
- int main()
- {
- Unique_ptr a(new A);
- a->show_info();
-
- return 0;
- }
6.智能指针与自定义删除器及包装器的使用:
6.1为什么要定义删除器:
我们可以看一下上面几根智能指针的实现,都是delete ptr;并不是delete []ptr,如果我们不自定义删除的话,就会导致内存泄漏,如下:
- #include
- #include
-
- using namespace std;
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
- };
-
- int main()
- {
- shared_ptr a(new A[5]);
-
- return 0;
- }
结果图:

分析:由图可知,构造了五次,析构了一次,造成了内存泄漏,这就是为什么我们要进行用删除器。
6.2自定义的删除器(函数对象):
- #include
- #include
-
- using namespace std;
- template <class T>
- class Deleter
- {
- public:
- void operator()(T* p)
- {
- delete [] p;
- }
- };
-
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
- };
-
- int main()
- {
- shared_ptr a(new A[5],Deleter());
-
- return 0;
- }
结果图:

6.3自定义的删除器(匿名6.4函数对象):
- #include
- #include
-
- using namespace std;
- template <class T>
- class Deleter
- {
- public:
- void operator()(T* p)
- {
- delete [] p;
- }
- };
-
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
- };
-
- int main()
- {
- shared_ptr a(new A[5],[](A* p){
- delete [] p;
- });
-
- return 0;
- }
结果图:

6.4智能指针的C++官方提供的释放连续空间的删除器
注意:每个智能指针删除所放的位置不一定相同,可以自己去查一下c++官方手册。
- #include
- #include
-
- using namespace std;
- template <class T>
- class Deleter
- {
- public:
- void operator()(T* p)
- {
- delete [] p;
- }
- };
-
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
- };
-
- int main()
- {
- unique_ptr> a(new A[5]);
- //unique_ptr> a(new A[5]);//这个也是可以的
- return 0;
- }
结果图:

6.5包装一个匿名函数对象的类型:
- #include
- #include
- #include
-
- using namespace std;
- template <class T>
- class Deleter
- {
- public:
- void operator()(T* p)
- {
- delete [] p;
- }
- };
-
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
- };
-
- int main()
- {
- unique_ptrvoid (A*)>> a(new A[5],Deleter());
-
- return 0;
- }
6.5.1包装器的使用:可以提升函数的效率,就想变成()函数一样,效率很高。
- #include
- #include
- #include
-
- using namespace std;
- template <class T>
- class Deleter
- {
- public:
- void operator()(T* p)
- {
- delete [] p;
- }
- };
-
-
- class A
- {
- public:
- A()
- {
- cout<<"A的构造"<
- }
-
- ~A()
- {
- cout<<"A的析构"<
- }
- };
-
- void show_info()
- {
- cout<<"加油"<
- }
-
- int main()
- {
- function<void ()> f=show_info;
- f();
-
- return 0;
- }
-
相关阅读:
vue @cliick.stop @click.prevent @click.self
一文带你深入了解JVM性能调优以及对JVM调优的全面总结
【探索Linux】—— 强大的命令行工具 P.8(进程优先级、环境变量)
UDS诊断入门
python3GUI--详细讲解一个QQ音乐组件的制作By:PyQt5(详细介绍、附源代码)
ROS Turtlebot3多机器人编队导航仿真
带你走进Cflow (一)
Node-v14.20.0 Windows下的环境变量配置
在2024年WWDC大会上,苹果宣布了其全新的“Apple Intelligence”AI功能以及ISO18功能
如何正确的清理C盘
-
原文地址:https://blog.csdn.net/a2998658795/article/details/126069592