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

C++——智能指针

智能指针

布隆过滤器 (1)

文章目录

  • 智能指针
      • 内存泄漏
      • 智能指针解决内存泄漏问题
      • 智能指针的使用及原理
        • RAII
        • 智能指针对象的拷贝问题
    • C++中的智能指针
      • auto_ptr
      • unique_ptr
      • shared_ptr
      • weak_ptr
      • 定制包装器
      • C++11和boost中智能指针的关系

内存泄漏

  1. 什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对 该段内存的控制,因而造成了内存的浪费。

  2. 内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现 内存泄漏会导致响应越来越慢,最终卡死。

C/C++程序中一般我们关心两种方面的内存泄漏:

  • 堆内存泄漏(Heap leak)

堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一 块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分 内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

  • 系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放 掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

内存泄漏很常见,解决方案通常分为两种:

  1. 事前预防型。如智能指针等。
  2. 事后查错型。如泄 漏检测工具。

智能指针解决内存泄漏问题

场景:main函数里调用func函数,在func函数里申请了一个int大小的空间,然后调用div函数,在div函数进行除法操作,若出现除零,则直接抛异常,直接跳转到main函数捕获异常,从而没有释放掉在func函数内申请的资源,即内存泄漏。

#include<iostream>
#include<vector>
using namespace std;int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{int* ptr = new int;//new了一个int大小的资源cout << div() << endl;delete ptr;//因为抛异常导致这个int资源没有被正常释放cout << "delete ptr" << endl;
}
int main()
{try{func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

解决方法一:在func函数内捕获一次异常,进行对申请资源的释放后,再将异常抛出,让外层栈帧去捕获解决异常。

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{	int* ptr = new int;//new了一个int大小的资源
try {cout << div() << endl;
}
catch (...)
{delete ptr;//因为抛异常导致这个int资源没有被正常释放cout << "delete ptr" << endl;throw;
}}
int main()
{try{func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

image-20230903191348146

解决方案二:让智能指针对资源进行管理,调用智能指针的构造函数申请资源,调用智能指针的析构函数释放资源。

template<class T>
class smartptr
{
public:smartptr(T* ptr=nullptr) :_ptr(ptr){}~smartptr(){if (_ptr){cout << "delete _ptr" << endl;delete _ptr;}}// 像指针一样T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void func()
{	smartptr<int> sma(new int);cout << div() << endl;	
}
int main()
{try{func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

image-20230903192627650

  • 在构造sma对象时,让smartptr的构造函数去申请资源。
  • 当退出func函数这层栈帧时,sma对象的生命周期结束,自动调用smartptr的析构函数去释放资源。
  • 将申请到的资源交给一个smartptr对象进行管理,这样无论是否出现除零都能正常释放资源。
  • 此外为了让smartptr对象能够像原生指针一样使用,还需要对*->运算符进行重载。

智能指针的使用及原理

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内 存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做 法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

需要注意的是:

  1. 在对象构造时获取资源,在对象析构时释放资源,即要具备RAII特性。
  2. *->运算符进行重载,使得对象具有像指针一样的行为。
  3. 智能指针对象的拷贝问题。

智能指针对象的拷贝问题

int main()
{smartptr<int> sm1(new int);smartptr<int> sm2(sm1);//拷贝构造smartptr<int> sm3(new int);//赋值重载sm3 = sm1;return 0;
}

image-20230903194741686

  • 编译器默认生成的拷贝构造是对内置类型完成值拷贝(浅拷贝),因此sm2对象对sm1对象的拷贝构造,是对sm1对象的资源的地址拷贝过来,即sm1和sm2对同一份资源进行管理,当退出栈帧对象的生命周期结束时,sm1和sm2一起会对一份资源析构两次,造成越界问题。
  • 编译器默认生产的拷贝赋值函数是对内置类型完成值拷贝(浅拷贝)。因此将sm1对象赋值給sm3对象,是将sm1管理资源的地址赋值給sm3的_ptr,即sm1和sm3对同一份资源进行管理,当退出栈帧对象的生命周期结束时,sm1和sm3一起会对同一份资源析构两次,造成越界问题。

智能指针要模拟出原生指针的行为,而我们将一个指针赋值給另一个指针,其目的就是让两个指针对同一份资源进行管理,但单纯的浅拷贝会导致空间多次释放,因此根据实现的场景不同,衍生出不同的智能指针。

C++中的智能指针

auto_ptr

auto_ptr的实现目的:对资源的管理权进行转移

auto_ptr是C++98中引入的智能指针,auto_ptr通过管理权转移的方式解决智能指针的拷贝问题,保证一个资源在任何时刻都只有一个对象在对其进行管理,这时同一个资源就不会被多次释放了。

auto_ptr的模拟实现

template<class T>
class Auto_ptr
{
public:Auto_ptr(T* ptr=nullptr):_ptr(ptr){}~Auto_ptr() { cout << "delete _ptr" << endl; delete _ptr; }T* operator->(){return _ptr;}T& operator*(){return *_ptr;}Auto_ptr(Auto_ptr<T>& tp)//拷贝构造:_ptr(tp._ptr){tp._ptr = nullptr;//悬空}Auto_ptr& operator=(Auto_ptr<T>& tp){if (this!=&tp)//判断是否是自己赋值給自己{if (_ptr){delete _ptr;_ptr = nullptr;}_ptr = tp._ptr;//转移资源管理权tp._ptr = nullptr;//悬空}return *this;}
private:T* _ptr;
};

简易auto_ptr实现思路:

  1. 在构造函数获取资源,在析构函数释放资源,利用对象的生命周期管理资源。
  2. *->运算符进行重载,使得对象具有像指针一样的行为。
  3. 在拷贝构造函数中,将传入对象的资源来构造当前对象,然后将传入对象管理资源的指针置空。
  4. 在拷贝赋值函数中,,先将当前对象的资源释放,然后将传入对象的资源来构造当前对象,最后将传入对象管理资源的指针置空。

测试代码

int main()
{Auto_ptr<int> apt1(new int(1));*apt1 = 10;Auto_ptr<int> apt2(apt1);Auto_ptr<int> apt3(new int(3));apt3 = apt2;return 0;
}

image-20230903204754573

image-20230903205008489

unique_ptr

unique_ptr的实现目的:防止拷贝

通过防止在智能指针之间互相拷贝,暴力的解决了多个智能指针对同一块资源进行释放的问题。

template<class T>
class Unique_ptr
{
public:Unique_ptr(T* ptr):_ptr(ptr){}~Unique_ptr(){if (_ptr){cout << "delete _ptr" << endl;delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}Unique_ptr<T>& operator=(const Unique_ptr<T>& upt) = delete;//禁用拷贝构造Unique_ptr(const Unique_ptr<T>& upt) = delete;//禁用拷贝赋值
private:T* _ptr;
};int main()
{Unique_ptr<int> upt1(new int(1));cout << *(upt1) << endl;Unique_ptr<int> upt2(upt1);Unique_ptr<int> upt3(new int(3));upt3 = upt1;return 0;
}

简易unique_ptr实现思路:

  1. 在构造函数获取资源,在析构函数释放资源,利用对象的生命周期管理资源。
  2. *->运算符进行重载,使得对象具有像指针一样的行为。
  3. 用C++98的方式将拷贝构造函数和拷贝赋值函数声明为私有,或者用C++11的方式在这两个函数后面加上=delete,防止外部调用。

image-20230904132449297

shared_ptr

share_ptr的实现目的:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

namespace s
{template<class T>class Share_ptr{public:Share_ptr(T* ptr = nullptr) :_ptr(ptr), _pcount(new int(1)) {}Share_ptr<T>& operator=(const Share_ptr<T>& spt)//赋值重载{if (_ptr != spt._ptr){Release();_ptr = spt._ptr;_pcount = spt._pcount;Addpcount();}return *this;}int use_count(){return *_pcount;}void Addpcount(){(*_pcount)++;}Share_ptr(const Share_ptr<T>& spt)//拷贝构造:_ptr(spt._ptr), _pcount(spt._pcount){Addpcount();}void Release(){if (--(*_pcount) == 0 && _ptr){//计数为0,释放资源cout << "delete _ptr" << endl;delete _ptr;delete _pcount;}}~Share_ptr(){Release();}T* operator->(){return _ptr;}T* get(){return _ptr;}T& operator*(){return *_ptr;}private:T* _ptr;int* _pcount;};
}int main()
{s::Share_ptr<int> spt1(new int(1));cout << "spt1 pcount: " << spt1.use_count() << endl;s::Share_ptr<int> spt2(spt1);cout << "spt2 pcount: " << spt2.use_count() << endl;s::Share_ptr<int> spt3(new int(3));spt3 = spt1;cout << "spt3 pcount: " << spt3.use_count() << endl;return 0;
}

image-20230904200615577

简易auto_ptr实现思路:

  1. Shared_ptr在其内部,给每个资源都维护了着一份计数_pcount,用来记录该份资源被几个对象共 享。
  2. 在构造函数中获取资源,并且设置计数器_pcount为1,表示当前只有自己在管理该资源。
  3. 在拷贝构造函数中,要与传入的对象一起管理它所管理的资源,因此同时将该资源对应的计数器++
  4. 在拷贝赋值函数中,若在管理资源需要先放弃管理当前资源,即先--_pcount,然后与传入对象一起管理传入对象所管理的资源,即++ _pcount。
  5. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减 一。如果引用计数为0,说明自己是最后一个使用该资源的对象,这时候必须由自己释放资源;如果引用计数不为0,说明还有其他对象使用该资源,不能释放资源,否则其他对象就成野指针了
  6. *->运算符进行重载,使得对象具有像指针一样的行为。

为什么share_ptr的引用计数要放在堆上?

  • 该引用计数不能是内置函数成员int类型的数据,这样会导致每个对象都有一个引用计数,而当多个对象管理同一个资源时,这些对象所用的引用计数应该是同一个。

image-20230904203433565

  • 该引用计数也不能是静态变量,静态变量会造成所有share_ptr类型的对象所共用同一个引用计数,那么当管理不同资源时就出计数问题。

image-20230904203807749

  • 因此将shared_ptr的引用计数设置为一个指针,指针指向一个计数器,当一个资源第一次被管理时就去堆区开辟一块空间用于存储其对应的引用计数,而当前其他对象也管理该资源时,除了将该资源交给对象外,该指针也要交给它,其他对象就能拿到该引用计数。
  • 这时候管理同一个资源的多个对象所访问到的引用计数就是同一个,管理不同资源的对象所访问的引用计数不是同一个,等同于资源与其引用计数一一对应起来。

image-20230904204607295

std::shared_ptr的线程安全问题

  1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2。这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁的,也就是说引用计数的操作是线程安全的。
  2. . 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。

首先验证C++库里的shared_ptr

#include<iostream>
#include<vector>
#include<memory>
#include<mutex>
#include<thread>
using namespace std;struct Date
{
public:int _year = 0;int _month = 0;int _day = 0;
};
void test_shared_ptr1()
{int n = 50000;mutex mtx;std::shared_ptr<Date> sp1(new Date);thread t1([&](){for (int i = 0; i < n; ++i){std::shared_ptr<Date> sp2(sp1);//mtx.lock();sp2->_year++;sp2->_day++;sp2->_month++;//	mtx.unlock();}});thread t2([&](){for (int i = 0; i < n; ++i){std::shared_ptr<Date> sp3(sp1);//	mtx.lock();sp3->_year++;sp3->_day++;sp3->_month++;//	mtx.unlock();}});t1.join();t2.join();cout << sp1.use_count() << endl;cout << sp1.get() << endl;cout << sp1->_year << endl;cout << sp1->_month << endl;cout << sp1->_day << endl;
}int main()
{test_shared_ptr1();
}
  • 首先创建一个sp1对象,对象管理Date结构体,结构体里有三个成员变量,_year、 _month、 _day。
  • 在线程thread1里sp2对象对sp1对象进行拷贝构造,即sp2也同sp1一起管理同一个Date结构体,并且对里面的成员变量进行加加,循环500次;
  • 在线程thread2里sp3对象对sp1对象进行拷贝构造,即sp3也同sp1、sp2一起管理同一个Date结构体,并且对里面的成员变量进行加加,循环500次;
  • 即sp2和sp3两个对象总和对Date对象里的成员变量加加1000次,遍历完后可以看到三个参数都为1000(如下图)

image-20230905142908596

  • 经过多次实验,可以看到存在成员对象不为1000的结果,说明了shared_ptr存在线程安全问题。其根本在于多线程访问同一块资源时,可能存在多个线程同时对同一个成员变量进行自增或自减操作,而其++操作不具备原子性。因此需要借助锁,将++操作的代码划分为临界区,互斥锁将临界区保护起来,每次只允许一个线程进入临界区,访问临界资源。

image-20230905143447807

	struct Date
{
public:int _year = 0;int _month = 0;int _day = 0;
};void test_shared_ptr1()
{int n = 500;mutex mtx;std::shared_ptr<Date> sp1(new Date);thread t1([&](){for (int i = 0; i < n; ++i){std::shared_ptr<Date> sp2(sp1);mtx.lock();sp2->_year++;sp2->_day++;sp2->_month++;mtx.unlock();}});thread t2([&](){for (int i = 0; i < n; ++i){std::shared_ptr<Date> sp3(sp1);mtx.lock();sp3->_year++;sp3->_day++;sp3->_month++;mtx.unlock();}});t1.join();t2.join();cout << sp1.use_count() << endl;cout << sp1.get() << endl;cout <<"sp1->_year: " << sp1->_year << endl;cout <<"sp1->_month: " << sp1->_month << endl;cout <<"sp1->_year: " << sp1->_day << endl;
}int main()
{test_shared_ptr1();
}

加互斥锁解决shared_ptr的线程安全问题

要解决引用计数的线程安全问题,本质就是让对引用计数的自增和自减变成一个原子操作,因此对引用计数的操作进行加锁保护,将对引用计数的操作划分为临界区,每次只允许一个线程进入临界区做引用计数操作。

namespace s
{template<class T>class Share_ptr{public:Share_ptr(T* ptr = nullptr) :_ptr(ptr), _pcount(new int(1)),_mut(new mutex) {}Share_ptr<T>& operator=(const Share_ptr<T>& spt)//赋值重载{if (_ptr != spt._ptr){Release();_ptr = spt._ptr;_pcount = spt._pcount;_mut = spt._mut;Addpcount();}return *this;}int use_count(){return *_pcount;}void Addpcount(){_mut->lock();(*_pcount)++;_mut->unlock();}Share_ptr(const Share_ptr<T>& spt)//拷贝构造:_ptr(spt._ptr), _pcount(spt._pcount),_mut(spt._mut){Addpcount();}void Release(){bool flag = false;_mut->lock();if (--(*_pcount) == 0 && _ptr){//计数为0,释放资源cout << "delete _ptr" << endl;delete _ptr;delete _pcount;flag = true;}_mut->unlock();if (flag == true){delete _mut;}}~Share_ptr(){Release();}T* operator->(){return _ptr;}T* get(){return _ptr;}T& operator*(){return *_ptr;}private:T* _ptr;int* _pcount;mutex* _mut;};
}
  • 在shared_ptr类中新增互斥锁,为了让管理同一份资源的多个对象访问到的是同一个互斥锁,管理不同资源的对象访问到的不是同一个互斥锁,互斥锁需要在堆区创建。
  • 在拷贝构造函数和拷贝赋值函数中,除了将对应资源和引用计数交给当前对象时,还需要将其资源对应互斥锁也交给当前对象。
  • 当一个资源对应的引用计数为0时,,除了将对应资源和引用计数释放外,还需要将其资源对应的互斥锁释放。
  • 在拷贝构造函数、拷贝赋值函数、析构函数中需要对引用计数做自增或自减操作,可以将其操作封装为Addpcount函数、Release函数,然后只需要在这两个函数中进行加锁保护。
  • shared_ptr只需要保证引用计数的线程安全问题,而管理资源的线程安全问题由管理这块资源的操作者来保证

shared_ptr的循环引用问题

struct ListNode
{int _data=0;s::Share_ptr<ListNode> _prev;s::Share_ptr<ListNode> _next;~ListNode() { cout << "~ListNode()" << endl; }
};
int main()
{s::Share_ptr<ListNode> node1(new ListNode);s::Share_ptr<ListNode> node2(new ListNode);cout << node1.use_count() << endl;cout << node2.use_count() << endl;node1->_next = node2;node2->_prev = node1;cout << node1.use_count() << endl;cout << node2.use_count() << endl;return 0;
}

image-20230905201042714

  • 新建一个listNode对象,让shared_ptr类node1对象管理,此时node1对应的引用计数为1,即listNode资源被一个shared_ptr对象管理。
  • 新建一个listNode对象,让shared_ptr类node2对象管理,此时node2对应的引用计数为1,即listNode资源被一个shared_ptr对象管理。
  • 此时node1和node2的引用计数都为1,我们不需要手动释放资源。在退出栈帧的时候会调用析构函数进行资源释放

image-20230905201101396

  • 让node1的next指向node2,即node1和node2一起来管理node2所管理的资源,因此node2的use_count等于2。
  • 让node2的prev指针指向node1,即node1和node2一起来管理node1所管理的资源,因此node1的use_count等于2。

image-20230905202103752

  • 在程序结束时,会调用node1和node2的析构函数,此时node1的use_count减减等于1,node2的use_count减减等于1;但node1无法被释放,原因在于node2的prev指针指向node1,想要释放node1需要先释放node2的prev指针,释放node2的prev指针的前提是释放node2;而node2也无法被释放,原因在于node1的next指针指向node2,想要释放node2需要先释放node1的next指针,释放node1的next指针的前提是释放node1。因此造成了循环引用问题。

weak_ptr

weak_ptr的作用为:

  • 构造出来的weak_ptr对象与shared_ptr对象管理同一份资源,但不会增加这块资源对应的引用计数。
  • weak_ptr支持用shared_ptr对象来构造weak_ptr对象
  • weak_ptr不是用来管理资源释放的,它主要是用来解决shared_ptr的循环引用问题。
namespace t
{template<class T>
class weak_ptr
{
public:weak_ptr() :_ptr(nullptr) {}weak_ptr<T>& operator=( s::Share_ptr<T>& spt)//赋值重载{_ptr = spt.get();return *this;}weak_ptr(const s::Share_ptr<T>& spt)//拷贝构造:_ptr(spt.get()){}T* operator->(){return _ptr;}T& operator*(){return *_ptr;}private:T* _ptr;
};
}
  • 提供一个无参的构造函数。
  • 拷贝构造函数用shared_ptr构造weak_ptr,把shared_ptr的指针传递給weak_ptr,weak_ptr也能够管理shared_ptr所管理的对象。
  • 赋值重载函数用shared_ptr构造weak_ptr,把shared_ptr的指针传递給weak_ptr,weak_ptr也能够管理shared_ptr所管理的对象。
  • *->运算符进行重载,使得对象具有像指针一样的行为。
  • shared_ptr提供一个get函数,用于获取指针管理资源。
struct ListNode
{int _data = 0;t::weak_ptr<ListNode> _prev;t::weak_ptr<ListNode> _next;~ListNode() { cout << "~ListNode()" << endl; }
};
int main()
{s::Share_ptr<ListNode> node1(new ListNode);s::Share_ptr<ListNode> node2(new ListNode);cout << " node1.use_count: " << node1.use_count() << endl;cout << " node2.use_count: " << node2.use_count() << endl;node1->_next = node2;node2->_prev = node1;cout << " node1.use_count: " << node1.use_count() << endl;cout << " node2.use_count: " << node2.use_count() << endl;return 0;
}

image-20230905205547763

可以看到此时资源正常释放了。

定制包装器

当智能指针的生命周期结束,所有的智能指针的析构函数都默认以delete的方式将资源释放。实际上这种方式并不好,因为不是所有智能指针所管理的资源都是以new的方式创建的。比如智能指针可能管理以new[]方式创建的资源,也管理的是一个文件指针。

int main()
{s::Share_ptr<int> sp(new int[10]);s::Share_ptr<int> sp1(fopen("test.cpp", "r"));return 0;
}
  • 在生命周期结束时,对于以new[]方式创建的资源进行delete会造成程序崩溃,因为以new[]申请到的资源必须以delete[]的方式释放。而以fopen打开的文件指针必须以fclose的方式进行关闭。

定制删除器的实现问题

  • C++标准库中实现shared_ptr时是分成了很多个类的,因此C++标准库中可以将删除器的类型设置为构造函数的模板参数,然后将删除器的类型在各个类之间进行传递。
  • 我们是直接用一个类来实现share_ptr,因此不能将定制删除器的类型设置为构造函数的模板参数。删除器需要在析构函数的Release函数中使用,因此需要用一个成员变量将器删除器保存下来,而在定义这个成员变量时就需要指定删除器的类型,因此就需要給shared_ptr类再增加一个模板参数,在构造shared_ptr对象时就指定删除器的类型。然后增加一个支持传入删除器的构造函数,在构造shared_ptr对象时就传递删除器,在释放资源时就调用该删除器即可。设置一个默认删除器,当用户定义shared_ptr对象时不传入删除器,就默认以delete的方式释放资源。
	template<class T>class default_delete{public:void operator()(T* ptr){cout << "delete ptr" << endl;delete ptr;}};template<class T>class Delarry{public:void operator()( T* arr){cout << "delete[]" << arr << endl;delete[]arr;}};template<class T,class D=default_delete<T>>class Share_ptr{public:Share_ptr(T* ptr ,D del) :_ptr(ptr), _pcount(new int(1)),_mut(new mutex),_del(del) {}void Release(){bool flag = false;_mut->lock();if (--(*_pcount) == 0 && _ptr){//计数为0,释放资源cout << "delete _ptr" << endl;_del( _ptr);//调用对应制定删除器释放资源delete _pcount;flag = true;}_mut->unlock();if (flag == true){delete _mut;}}~Share_ptr(){Release();}//......private:T* _ptr;int* _pcount;mutex* _mut;D _del;
class Fclose
{
public:void operator()(FILE* ptr){cout << "fclose ptr" << endl;fclose(ptr);}
};int main()
{s::Share_ptr<int,s::Delarry<int>> sp(new int[10],s::Delarry<int>());s::Share_ptr<FILE, function<void(FILE*)>> sp2(fopen("test.cpp", "r"), [](FILE* ptr) {cout << "fclose: " << ptr << endl;fclose(ptr); });return 0;
}

C++11和boost中智能指针的关系

  1. C++ 98 中产生了第一个智能指针auto_ptr。
  2. . C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr。
  3. . C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。
  4. C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost 的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。

相关文章:

C++——智能指针

智能指针 文章目录 智能指针内存泄漏智能指针解决内存泄漏问题智能指针的使用及原理RAII智能指针对象的拷贝问题 C中的智能指针auto_ptrunique_ptrshared_ptrweak_ptr定制包装器C11和boost中智能指针的关系 内存泄漏 什么是内存泄漏&#xff1a;内存泄漏指因为疏忽或错误造成程…...

CVE-2023-3836:大华智慧园区综合管理平台任意文件上传漏洞复现

文章目录 CVE-2023-3836&#xff1a;大华智慧园区综合管理平台任意文件上传漏洞复现0x01 前言0x02 漏洞描述0x03 影响范围0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 CVE-2023-3836&#xff1a;大华智慧园区综合管理平台任意文件上传漏洞复现 0x01 前言 免责声…...

LAMP搭建WordPress

L linux A apache hhtpd M mysql/maridb P PHP1、 安装php yum -y install php php-fpm php-server php-mysql1.1、 启动php-fpm并自启 systemctl enable php-fpm --now[rootecs-1cee ~]# systemctl status php-fpm ● php-fpm.service - The PHP FastCGI Process ManagerLoa…...

【数学建模竞赛】预测类赛题常用算法解析

解析常见的预测类算法 灰色预测模型 灰色预测模型是一种利用少量的、不完全的信息&#xff0c;建立数学模型并进行预测的方法。该方法通过对系统行为特征的发展变化规律进行估计预测&#xff0c;同时也可以对行为特征的异常情况发生的时刻进行估计计算&#xff0c;并研究特定…...

OFDM 系统在 AWGN 信道下对不同载波频率偏移 (CFO) 的 BER 灵敏度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

go基础07-了解map实现原理并高效使用

对于C程序员出身的Gopher来说&#xff0c;map类型是和切片、interface一样能让他们感受到Go语言先进性的重要语法元素。map类型也是Go语言中最常用的数据类型之一。 go 中 map 怎么表现&#xff1f; 一些有关Go语言的中文教程或译本将map称为字典或哈希表&#xff0c;但在这里…...

SpringMVC进阶:常用注解、参数传递和请求响应以及页面跳转

目录 一、常用注解 1.1.RequestMapping 1.2.RequestParam 1.3.ModelAttribute 1.4.SessionAttributes 1.5.RequestBody 1.6.RequestHeader 1.7.PathVariable 1.8.CookieValue 二、参数传递 2.1.基础类型String 2.2.复杂类型 2.3.RequestParam 2.4.PathVariable 2…...

nacos - centos7.x环境单机与集群快速部署

参考官网:https://nacos.io/zh-cn/docs/what-is-nacos.html 官方集群部署手册:https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html 【单机部署】 1.下载 & 解压到安装目录 下载:wget -c https://github.com/alibaba/nacos/releases/download/2.1.2/nacos-ser…...

文心一言初体验,和ChatGPT语言理解能力比较

文章目录 第一个考验&#xff0c;语义理解第二个考验&#xff0c;历史问题的回答推荐阅读 百度旗下AI大模型文心一言宣布向全社会全面开放,所有用户都可以体验这款AI大模型了。要比较这两个语言模型&#xff0c;我们先设计好题目。 第一个考验&#xff0c;语义理解 题目1&…...

浏览器进程,性能指标,性能优化

目录 浏览器进程&#xff1a;多进程 主进程&#xff1a;显示、交互&#xff0c;增删进程 UI进程&#xff1a;控制地址栏、书签、前进后退 存储进程&#xff1a;cookie&#xff0c;webstorage&#xff0c;indexDB 渲染进程&#xff1a;每个标签页或窗口都有一个独立的渲染进…...

Python基础set集合定义与函数

set集合 集合的特点&#xff1a; 1.集合是无序 2.集合是去重 定义一个空集合 name_set set() 定义一个非空集合 name_set {a, b, c} 关系测试&#xff1a; 交集&#xff0c;并集&#xff0c;差集&#xff0c;对称差集 1.交集&#xff1a;intersection() 或者 & …...

【大数据之Kafka】九、Kafka Broker之文件存储及高效读写数据

1 文件存储 1.1 文件存储机制 Topic是逻辑上的概念&#xff0c;而partition是物理上的概念&#xff0c;每个partition对应于一个log文件&#xff0c;该log文件中存储的是Producer生产的数据。 Producer生产的数据会被不断追加到该log文件末端&#xff0c;为防止log文件过大导致…...

Android 使用Camera2 API 和 GLSurfaceView实现相机预览

GLSurfaceView 和 SurfaceView 是 Android 中用于显示图像的两个视图类&#xff0c;它们在实现方式和使用场景上有一些区别。 实现方式&#xff1a;GLSurfaceView 基于 OpenGL ES 技术实现&#xff0c;可以通过 OpenGL ES 渲染图像。而 SurfaceView 则是通过基于线程的绘制方式…...

说说IO多路复用

分析&回答 IO多路复用 I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流。直白点说&#xff1a;多路指的是多个socket连接&#xff0c;复用指的是复用一个…...

mysql 锁解决的办法

可以查看锁的信息,TRX_MYSQL_THREAD_ID 为processlist的表中的会话id,用于kill select trx_id,trx_state,trx_started,trx_requested_lock_id,trx_wait_started,trx_weight,trx_mysql_thread_id,trx_query from innodb_trx 可以查看锁的模式&#xff0c;类型&#xff0c;锁的表…...

C++零碎记录(五)

9. 静态成员 ① 静态成员就是在成员变量和成员函数前加上关键字static&#xff0c;称为静态成员。 ② 静态成员分为&#xff1a; 1. 静态成员变量 --所有对象共享同一份数据 --在编译阶段分配内存 --类内声明&#xff0c;类外初始化 2. 静态成员函数 --所有对象共享同一个函数…...

玩转Mysql系列 - 第16篇:变量详解

这是Mysql系列第16篇。 环境&#xff1a;mysql5.7.25&#xff0c;cmd命令中进行演示。 代码中被[]包含的表示可选&#xff0c;|符号分开的表示可选其一。 我们在使用mysql的过程中&#xff0c;变量也会经常用到&#xff0c;比如查询系统的配置&#xff0c;可以通过查看系统变…...

Windows云服务器 PHP搭建网站外网无法访问的问题

前言&#xff1a;本人在华为云上租了一台windows的云主机&#xff0c;可以远程访问桌面的那种&#xff0c;然后想搭个网站&#xff0c;最开始想到的是IIS&#xff0c;测试了下用html的文件&#xff0c;没有问题。但是&#xff0c;php文件却不能用&#xff0c;因为少了PHP环境。…...

TuyaOS Sensor Hub组件介绍

文章目录 Sensor Hub 设计思想分层设计Sensor Hub 层(tdl)Sensor Driver 层(tdd) 传感数据元素类型抽象传感器采集策略 Sensor Hub 对上数据与接口数据结构1. 数据读取的触发模式2. 元素型数据订阅规则3. 数据就绪通知回调4. 传感设备信息 应用接口1. 创建传感器实例2. 启动传感…...

【实战】React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(总结展望篇)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…...

Leetcode.321 拼接最大数

题目链接 Leetcode.321 拼接最大数 hard 题目描述 给定长度分别为 m m m 和 n n n 的两个数组&#xff0c;其元素由 0 ∼ 9 0 \sim 9 0∼9 构成&#xff0c;表示两个自然数各位上的数字。现在从这两个数组中选出 k k k ( k ≤ m n ) (k \leq m n) (k≤mn) 个数字拼接成…...

数学建模竞赛常用代码总结-PythonMatlab

数学建模过程中有许多可复用的基础代码&#xff0c;在此对 python 以及 MATLAB 中常用代码进行简单总结&#xff0c;该总结会进行实时更新。 一、文件读取 python (pandas) 文件后缀名&#xff08;扩展名&#xff09;并不是必须的&#xff0c;其作用主要一方面是提示系统是用…...

在Ubuntu上安装CUDA和cuDNN以及验证安装步骤

在Ubuntu上安装CUDA和cuDNN以及验证安装步骤 本教程详细介绍了如何在Ubuntu操作系统上安装CUDA&#xff08;NVIDIA的并行计算平台&#xff09;和cuDNN&#xff08;深度神经网络库&#xff09;&#xff0c;以及如何验证安装是否成功。通过按照这些步骤操作&#xff0c;您将能够…...

SecureCRT ssh链接服务器

SecureCRT通过密钥进行SSH登录 说明&#xff1a; 一般的密码方式登录容易被密码暴力破解。所以一般我们会将 SSH 的端口设置为默认22以外的端口&#xff0c;或者禁用root账户登录。其实可以通过密钥登录这种方式来更好地保证安全。 密钥形式登录的原理是&#xff1a;利用密钥…...

linux之perf(3)top实时性能

Linux之perf(3)top实时性能 Author&#xff1a;Onceday Date&#xff1a;2023年9月3日 漫漫长路&#xff0c;才刚刚开始… 注&#xff1a;该文档内容采用了GPT4.0生成的回答&#xff0c;部分文本准确率可能存在问题。 参考文档: Tutorial - Perf Wiki (kernel.org)perf-to…...

【linux命令讲解大全】076.pgrep命令:查找和列出符合条件的进程ID

文章目录 pgrep补充说明语法选项参数实例 从零学 python pgrep 根据用户给出的信息在当前运行进程中查找并列出符合条件的进程ID&#xff08;PID&#xff09; 补充说明 pgrep 命令以名称为依据从运行进程队列中查找进程&#xff0c;并显示查找到的进程ID。每一个进程ID以一个…...

微信小程序开发---条件渲染和列表渲染

目录 一、条件渲染 &#xff08;1&#xff09;基本使用 &#xff08;2&#xff09;block &#xff08;3&#xff09;hidden 二、列表渲染 &#xff08;1&#xff09;基本使用 &#xff08;2&#xff09;手动指定索引和当前项的变量名 &#xff08;3&#xff09;wx:key的…...

【ES6】require、export和import的用法

在JavaScript中&#xff0c;require、export和import是Node.js的模块系统中的关键字&#xff0c;用于处理模块间的依赖关系。 1、require&#xff1a;这是Node.js中引入模块的方法。当你需要使用其他模块提供的功能时&#xff0c;可以使用require关键字来引入该模块。例如&…...

Vue + Element UI 前端篇(九):接口格式定义

接口请求格式定义 前台显示需要后台数据&#xff0c;我们这里先把前后端交互接口定义好&#xff0c;没有后台的时候&#xff0c;也方便用mock模拟。 接口定义遵循几个规范&#xff1a; 1. 接口按功能模块划分。 系统登录&#xff1a;登录相关接口 用户管理&#xff1a;用户…...

部署Django报错-requires SQLite 3.8.3 or higher

记一次CentOS7部署Django项目时的报错 问题出现 在部署测试环境时&#xff0c;有需要用到一个python的后端服务&#xff0c;要部署到测试环境中去 心想这不是so easy吗&#xff0c;把本地调试时使用的python版本及Django版本在服务器上对应下载好&#xff0c;然后直接执行命…...

创建网站公司 徐州/怎么样推广自己的公司

问题&#xff1a;向正在编辑的Word文档中插入图片时&#xff0c;会发现图片只显示了部分&#xff0c;其余部分被其上面的问题遮挡住 原因&#xff1a;可能是由于设置固定的行间距&#xff0c;导致图片被遮挡 解决方法&#xff1a; 选定插入的图片&#xff0c;在菜单栏中找到 “…...

可以自己做网站的软件下载/搜索引擎优化排名seo

概述 因为买不起高速卡&#xff0c;所以只能使用普通的内存卡&#xff0c;但是又怕内存卡坏掉&#xff0c;而且内存卡只有8g容量太小&#xff0c;正好我有一个usb3.0接口的128g u盘&#xff0c;虽然树莓派不支持usb3.0&#xff0c;但是这样子做总比一张内存卡快 镜像的选择 我不…...

hot插件 wordpress/短视频拍摄剪辑培训班

前言 互联网可信身份认证平台签发微信身份证。12月25日广州市公安局南沙区分局、腾讯等10余家单位共发微警云联盟并广州南沙成立&#xff0c;现场联盟成员单位共签署合作框架协议并签发全国首张微信身份证网证&#xff08;身份证网络版或电子版&#xff09;。该网证由公安部第一…...

郑州做企业网站哪家好/推广找客户平台

此处进行简单的分类&#xff0c;对于普通的网页爬取内容&#xff0c;如果没有登录界面可以直接使用Jsoup的API进行爬取&#xff1b; 如果网站是在打开目标也之前需要进行登录&#xff0c;此时需要先使用用户加密码实现登录获取Cookie然后进行登录&#xff1b; 本文就第二种方式…...

满屏网站做多大尺寸/无锡网站建设优化公司

RS232接口保护方案1. 防雷保护器的总体要求2. RS232 防雷过压 带电拔插 静电保护综合防护&#xff1a;器件选择&#xff1a;3. RS232 过压 带电拔插 静电保护&#xff1a;3.1. 不使用任何 TVS 器件&#xff0c;以 115200 波特率同时发送和接收数据&#xff0c;测量发送和接…...

做动态网站 和数据库交互/乔拓云建站平台

说实话&#xff0c;这是个老话题&#xff0c;有很多人都知道怎么传&#xff0c;但也有很多的朋友弄不清楚&#xff0c;折腾半天都没有结果&#xff0c;现在我在这里再分解一次。其实三个典型方法&#xff1a;1、利用QQ、微信传图片资料等&#xff1b;2、用电脑上软件&#xff0…...