C++ 中的智能指针

devtools/2024/11/24 8:59:29/

智能指针的作用:

        当使用普通指针来管理动态分配的内存时,程序员需要手动负责在合适的时候释放内存,否则可能会导致内存泄漏。而智能指针通过自动管理内存的生命周期,在适当的时候自动释放内存,大大降低了内存管理的复杂性和出错的可能性。智能指针主要有以下三种类型:

share_ptr(共享智能指针)

        不能使用同一个地址初始化智能指针

        构造函数初始化

#include<iostream>
#include<string>
#include<algorithm>
#include<memory>
using namespace std; 
int main(){shared_ptr<int>ptr1(new int(520));cout << "ptr1管理内存引用计数:" << ptr1.use_count() << endl;shared_ptr<char>ptr2(new char[520]);cout << "ptr2管理内存引用计数:" << ptr2.use_count() << endl;shared_ptr<int>ptr3;cout << "ptr3管理内存引用计数:" << ptr3.use_count() << endl;shared_ptr<int>ptr4(nullptr);cout << "ptr4管理内存引用计数:" << ptr4.use_count() << endl;return 0;
}

        如果智能指针被初始化了一块有效内存,那么这块内存的引用计数加1,如果智能指针没有被初始化或者被初始化为nullptr空指针,引用计数为0。另外,不要使用一个原始指针初始化多个shared_ptr

        拷贝构造和移动构造初始化

#include<iostream>
#include<string>
#include<algorithm>
#include<memory>
using namespace std;
int main()
{shared_ptr<int>ptr1(new int(520));cout << "ptr1管理内存引用计数:" << ptr1.use_count() << endl;shared_ptr<int>ptr2(ptr1);cout << "ptr2管理内存引用计数:" << ptr2.use_count() << endl;shared_ptr<int>ptr3 = ptr1;cout << "ptr3管理内存引用计数:" << ptr3.use_count() << endl;shared_ptr<int>ptr4(move(ptr1));cout << "ptr4管理内存引用计数:" << ptr4.use_count() << endl;shared_ptr<int>ptr5 = move(ptr2);cout << "ptr5管理内存引用计数:" << ptr5.use_count() << endl;return 0;
}

std::make shared的初始化

        通过 c++11 提供的 std::make shared()就可以完成内存对象的创建并将其初始化给智能指针

#include<iostream>
#include<string>
#include<algorithm>
#include<memory>
using namespace std; 
class Test{
public:Test(){cout<<"无参构造" << endl;}Test(int x){cout << "有参构造" << endl;}Test(string str){cout << "string类型构造" << endl;}~Test(){cout << "析构函数" << endl;}
};
int main(){shared_ptr<int>ptr1 = make_shared<int>(520);shared_ptr<Test>ptr2 = make_shared<Test>();	shared_ptr<Test>ptr3 = make_shared<Test>(520);	shared_ptr<Test>ptr4 = make_shared<Test>("asdjas;dlhas");	shared_ptr<Test>ptr5 = move(ptr2); cout << "ptr1管理内存引用计数:" << ptr1.use_count() << endl;cout << "ptr2管理内存引用计数:" << ptr2.use_count() << endl;cout << "ptr3管理内存引用计数:" << ptr3.use_count() << endl;cout << "ptr4管理内存引用计数:" << ptr4.use_count() << endl;cout << "ptr5管理内存引用计数:" << ptr5.use_count() << endl; return 0;
}

        如果使用拷贝的方式初始化共享智能指针,这两个对象会同时管理同一块内存,堆内存对应的引用计数也会增加。如果使用移动构造的方式初始化智能指针对象,只是转让了内存的所有权,管理内存的对象不会增加,因此内存引用技术不会增加。

shareed_ptr的实现

#pragma once
#include<iostream>
using namespace std; 
template<typename T>//模板不可以多文件编程
class Ref {T* p;int n;
public:Ref(){p = nullptr;n = 0;}    Ref(T* p){This->p = p;n = 1;}void increase(){if(p)n++;}void reduce(){if(n > 0)n--;if (n == 0) {if(p)delete p;delete this;}}int useCount(){return n;}T* get(){return p;}
};template<typename T>
class Shared_ptr{Ref<T>* r;
public:Shared_ptr(){r = new Ref<T>();}Shared_ptr(T* p){r = new Ref<T>(p);}Shared_ptr(const Shared_ptr& o){this->r = o.r;if(r->get())r->increase();}Shared_ptr(Shared_ptr&& o){r = o.r;o.r = nullptr;}~Shared_ptr(){if(r)r->reduce();} Shared_ptr& operator =(const Shared_ptr& other){r->reduce();r = other.r;if (r->get())r->increase();return *this;}Shared_ptr& operator =(Shared_ptr&& other){r->reduce();r = other.r;other.r = nullptr;return *this;}int UseCount(){if (r)return r->useCount();else return 0;}T* get(){return r->p;}void reset(){r->reduce();r = nullptr;}void reset(T*p){r->reduce();r = new Ref<T>(p);}T& operator *(){return *r->get();}T* operator ->(){return r->get();} 
};

weak_ptr(弱引用智能指针)

        弱引用智能指针 std::weak ptr 可以看做是 shared ptr 的助手,它不管理 shared ptr 内部的指针。std::weak ptr 没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视shared ptr 中管理的资源是否存在。

        初始化

#include<iostream>
#include<memory>
using namespace std; 
int main() {shared_ptr<int>sp(new int); //构造了一个空weak_ptr对象weak_ptr<int>wp1;//通过一个空weak_ptr对象构造了另一个空weak_ptr对象weak_ptr<int>wp2(wp1);//通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象weak_ptr<int>wp3(sp);//通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象(隐式转换类型)weak_ptr<int>wp4;wp4 = sp;//通过一个weak_ptr对象构造了一个可用的weak_ptr实例对象weak_ptr<int>wp5;wp5 = wp3;    return 0;
}

use_count()

        通过调用std::weak_ptr类提供的use_count()方法可以获得当前所观察资源的引用计数

#include<iostream>
#include<memory>
using namespace std;
int main() {shared_ptr<int>sp(new int);weak_ptr<int>wp1;weak_ptr<int>wp2(wp1);weak_ptr<int>wp3(sp);weak_ptr<int>wp4;wp4 = sp;weak_ptr<int>wp5;wp5 = wp3;cout << "use_count:" << endl;cout << "wp1:" << wp1.use_count() << endl;cout << "wp2:" << wp2.use_count() << endl;cout << "wp3:" << wp3.use_count() << endl;cout << "wp4:" << wp4.use_count() << endl;cout << "wp5:" << wp5.use_count() << endl;return 0;
}

        通过打印结果可知,虽然弱引用智能指针wp3,wp4,wp5监测的资源是同一个,但是其引用技术没有变化,进一步证明了weak_ptr只是监测资源,并不管理资源

expired()

        通过调用std::weak_ptr类提供的expired()方法来判断观测的资源是否已经被释放

#include<iostream>
#include<memory>
using namespace std;
int main() {shared_ptr<int>shared(new int(10));weak_ptr<int>weak(shared);cout << "1.weak" << (weak.expired() ? "is" : "is not") << "expired" << endl;shared.reset();cout << "2.weak" << (weak.expired() ? "is" : "is not") << "expired" << endl;return 0;
}

        weak_ptr监测的就是shared_ptr管理得内存,当共享智能指针调用shared.reset();之后管理的资源被释放,因此weak.expired()函数的结果返回为true,表示监测得资源已经不存在了

lock()

        通过调用std::weak_ptr类提供的lock()方法来获取管理所检测资源的shared_ptr对象

#include<iostream>
#include<memory>
using namespace std;
int main() {shared_ptr<int>sp1,sp2;weak_ptr<int>wp;sp1 = std::make_shared<int>(520);wp = sp1;sp2 = wp.lock();cout << "use_count:" << wp.use_count() << endl;sp1.reset();cout << "use_count" << wp.use_count() << endl;sp1 = wp.lock();cout << "use_count:" << wp.use_count() << endl;cout << "sp1:" << *sp1 << endl;cout << "sp2:" << *sp2 << endl;return 0;
}

        sp2 = wp.lock();通过调用lock()方法得到一个用于管理weak_ptr对象所检测的资源共享智能指针对象,使用这个对象初始化sp2,此时所监测资源的引用计数为2

        sp1.reset();共享智能指针sp1被重置,weak_ptr对象所监测的资源引用计数减1

        sp1 = wp.lock();sp1重新被初始化,并且管理还是weak_ptr对象所监测的资源,因此引用计数加1

        共享智能指针对象sp1和sp2管理的是同一块内存,因此最终打印的内存中的结果是相同的,都是520

*返回管理this的shared_ptr

#include <iostream>
#include <memory>
using namespace std;
struct Test{shared ptr<Test>getSharedPtr(){return shared ptr<Test>(this);}~Test(){cout<<"析构函数"<< endl;}
};
int main(){shared ptr<Test>spl(new Test);cout<<"引用个数"<< spl.use count()<< endl;shared ptr<Test>sp2=sp1->getSharedPtr();cout<<"引用个数:"<<spl.use count()<< endl;return 0:
}

        通过输出的结果可以看到一个对象被析构两次,原因是:这个例子中使用同一个this构造了两个智能指针对象sp1和sp2,这二者之间是没有任何关系的,因为sp2并不是通过sp1初始化得到的实例对象。在离开作用域之后this将被构造的两个智能指针各自析构,导致重复释放内存

*循环引用问题

#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A{
public://shared_ptr<B> bptr; //错误weak_ptr<B> bptr;~A(){cout << "class TA is disstruct ..." << endl;}
};
class B{
public://shared_ptr<A> aptr;weak_ptr<A> aptr;~B(){cout << "class TB is disstruct ..." << endl;}
};int main(){shared_ptr<A> ap(new A);shared_ptr<B> bp(new B);cout << "A 的 引用计数: " << ap.use_count() << endl;cout << "B 的 引用计数: " << bp.use_count() << endl;ap->bptr = bp;bp->aptr = ap;cout << "A 的 引用计数: " << ap.use_count() << endl;cout << "B 的 引用计数: " << bp.use_count() << endl;return 0;
}

        共享智能指针ap,bp对A\B实例对象的引用计数变为2,在共享智能指针离开作用域之后引用计数只能减为1,这种情况下不会去删除智能指针管理的内存,导致A,B的实例对象不能被析构,最终造成内存泄漏。通过使用wea_ptr可以解决这个问题,只需将类A或类B的任意一个成员改为weak_ptr。

unique_ptr(独占智能指针)

        初始化:它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针,但不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr

#include<iostream>
#include<string>
#include<algorithm>
#include<memory>
using namespace std;
int main()
{unique_ptr<int>ptr(new int(10));//报错unique_ptr<int>ptr2 = ptr1;return ;
}

        unique_ptr不允许被复制,但是可以通过函数返回给其他的unique_ptr,还可以通过move()转移给其他的unique_ptr。还是一个unique_ptr独占一个地址。

        使用reset方法可以让unique_ptr解除对原始内存的管理,也可以用来初始化一个独占智能指针。

#include<iostream>
#include<string>
#include<algorithm>
#include<memory>
using namespace std;
int main()
{unique_ptr<int>ptr1(new int(10));unique_ptr<int>ptr2;ptr1.reset();//解除对原始内存的管理ptr2.reset(new int(250));//重新指定智能指针管理的原始内存return ;
}

        如果想要获取独占智能指针管理的原始地址,可以调用get()方法

#include<iostream>
#include<string>
#include<algorithm>
#include<memory>
using namespace std;
int main()
{unique_ptr<int>ptr1(new int(10));unique_ptr<int>ptr2 = move(ptr1);ptr2.reset(new int(250));cout << *ptr2.get() << endl;//得到内存地址中存储的实际数值250return ;
}

http://www.ppmy.cn/devtools/136513.html

相关文章

递归算法专题一>Pow(x, n)

题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public double myPow(double x, int n) {return n < 0 ? 1.0 / pow(x,-n) : pow(x,n); }private double pow(double x, int n){if(n 0) return 1.0;double tmp pow(x,n / 2);return n % 2 0 ? tmp * tmp : tmp …

HashMap底层原理

jdk1.8之后hashmap底层结构 jdk1.8之后&#xff0c;是哈希表数据结构&#xff0c;也可以说是数组链表或红黑树&#xff0c;下图是没有添加数据的一个hashmap。 现在开始添加数据&#xff0c;看下面具体步骤 put操作 如下&#xff0c;我们来简单看看hashmap的put源码&#xff…

基于YOLOv8深度学习的智慧农业果园果树柑橘类果实目标检测系统(PyQt5界面+数据集+训练代码)

近年来&#xff0c;随着人工智能和物联网技术的迅速发展&#xff0c;智慧农业已逐渐成为现代农业发展的核心方向之一。在这一领域&#xff0c;目标检测技术因其在精准农业中的广泛应用前景&#xff0c;尤其是在果园果树管理中的显著作用&#xff0c;而备受关注。果树的果实检测…

C++ STL - vector/list讲解及迭代器失效

vector 使用 vector 是一个动态数组. 构造/拷贝构造/赋值重载函数 int main() {// 是一个模板, 在实例化的时候, 需要指明类型std::vector<int> first; // 一个空的数组std::vector<int> second (4,100); // 设置初始空间大小为 4 个int, 全部初始化为 100std::v…

Linux: C语言解析域名

在上一篇博客 Linux: C语言发起 DNS 查询报文 中&#xff0c;自己构造 DNS 查询报文&#xff0c;发出去&#xff0c;接收响应&#xff0c;以二进制形式把响应的数据写入文件并进行分析。文章的最后留下一个悬念&#xff0c;就是写代码解析 DNS answer section 部分。本文来完成…

Spring Boot OA系统:企业资源规划的新选择

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

【UE5】使用基元数据对材质传参,从而避免新建材质实例

在项目中&#xff0c;经常会遇到这样的需求&#xff1a;多个模型&#xff08;例如 100 个&#xff09;使用相同的材质&#xff0c;但每个模型需要不同的参数设置&#xff0c;比如不同的颜色或随机种子等。 在这种情况下&#xff0c;创建 100 个实例材质不是最佳选择。正确的做…

【Next】中间件

概述 Next.js 的 中间件 (Middleware) 是一种在请求完成之前运行的函数&#xff0c;用于对入站请求进行处理和操作。它可以在路由匹配前执行逻辑&#xff0c;用于身份验证、请求重写、重定向、设置响应头等任务。 使用场景 身份验证&#xff1a;在用户访问页面前检查登录状态…