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

C++——智能指针

目录

一、RAII

二、auto_ptr

三、unique_ptr

四、shared_ptr


一、RAII

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

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

二、auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针,
auto_ptr的实现原理:管理权转移的思想

下面简化模拟实现了一份 auto_ptr 来了解它的原理:

namespace SmartPtr
{template<class T>class auto_ptr{public:auto_ptr(T* ptr):ptr_(ptr){}auto_ptr(auto_ptr<T>& ap) :ptr_(ap.ptr_){ap.ptr_ = nullptr;// 所有权转移}auto_ptr<T>& operator=(auto_ptr<T>& ap){// 检测是否为自己给自己赋值if (this != &ap){// 释放当前对象中资源if (ptr_)delete ptr_;// 转移ap中资源到当前对象中ptr_ = ap.ptr_;ap.ptr_ = nullptr;}return *this;}~auto_ptr(){if (ptr_){std::cout << "delete:" << ptr_ << std::endl;delete ptr_;}}// 像指针一样使用T& operator*(){return *ptr_;}T* operator->(){return ptr_;}private:T* ptr_;};
};

并且可以使用以下代码进行测试:

int main()
{using namespace std;// 测试基本功能int* p = new int(10);SmartPtr::auto_ptr<int> sp1(p);cout << "*sp1 = " << *sp1 << endl; // 测试 operator*// 测试所有权转移SmartPtr::auto_ptr<int> sp2(sp1);if (sp1.operator->() == nullptr)cout << "sp1 ownership transferred to sp2" << endl;cout << "*sp2 = " << *sp2 << endl;// 测试赋值操作符int* p2 = new int(20);SmartPtr::auto_ptr<int> sp3(p2);sp2 = sp3;if (sp3.operator->() == nullptr)cout << "sp3 ownership transferred to sp2" << endl;cout << "*sp2 after assignment = " << *sp2 << endl;return 0;
}

学习 auto_ptr 只是为了更了解智能指针的历史与后面的智能指针的改进, auto_ptr 基本算一个被时代遗弃的产物,所以尽量不要去使用 auto_ptr 管理内存资源。

三、unique_ptr

unique_ptr 是对 auto_ptr 的改进,它不再有所有权转移,它旨在独占所有权,即它只能有一个所有者。
下面来看一下简易版的 unique_ptr :

template<class T>class unique_ptr{public:unique_ptr(T *ptr):ptr_(ptr){}~unique_ptr(){std::cout << "delete unique_ptr" << std::endl;delete ptr_;}T* operator->(){return ptr_;}T& operator*(){return *ptr_;}private:unique_ptr(unique_ptr<T> &up) = delete;unique_ptr<T> operator=(unique_ptr<T >& up) = delete;private:T* ptr_;};

简单粗暴的将拷贝构造和赋值运算符重载给 delete ,保证了所有权的唯一性。

但是,没了拷贝构造或赋值重载,这就意味着 unique_ptr 就没办法 “赋值” 给其他 unique_ptr 吗?事实并非如此, unique_ptr 内部还有一个移动构造与移动赋值函数,简化如下:

// 移动构造函数
unique_ptr(unique_ptr<T>&& up): ptr_(up.ptr_)
{up.ptr_ = nullptr; // 转移所有权
}// 移动赋值操作符
unique_ptr<T>& operator=(unique_ptr<T>&& up)
{if (this != &up){delete ptr_; // 删除当前持有的对象ptr_ = up.ptr_; // 接收新对象的所有权up.ptr_ = nullptr;}return *this;
}

 我们仍可以使用 unique_ptr<int> p2(std::move(p1)) 类似的操作来进行所有权的转移。

学习到了这里,你有没有想过,为什么 auto_ptr 一定要使用所有权转移的概念,为什么 unique_ptr 一定要独享所有权,不允许多个指针指向同一个对象?

以下是可能发生的危害:

假设 unique_ptr 支持拷贝构造或赋值重载,会带来以下几个问题:

  • 双重释放问题:当多个 unique_ptr 指向同一个对象时,当其中一个 unique_ptr 被销毁,它会释放所指向的对象;而其他指向相同对象的 unique_ptr 也会在各自销毁时再次尝试释放同一对象,导致程序崩溃。

  • 资源泄漏问题:如果 unique_ptr 被拷贝而没有转移所有权,多个指针都会管理同一个对象,当某个 unique_ptr 释放对象后,其他 unique_ptr 依然会尝试访问已被销毁的资源,导致悬空指针问题。

四、shared_ptr

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

在 shared_ptr 中使用引用计数来管理资源,那么就需要使得指向同一份资源的指针可以访问同一块区域以调整引用计数,所以这里的计数器不能是简单的类成员,我们可以将它设置为指针,在不同 shared_ptr 使用时将指针赋值给它们。

同时,因为库中的 shared_ptr 使用互斥锁保证线程安全,我们这里也可以设置一个锁为成员变量,可以使用库中的 lock::guard ,类似于智能指针(RAII),可以自动解锁,在析构时也不需要单独释放锁:

	template<class T>class shared_ptr{public:shared_ptr(T* ptr):ptr_(ptr), pmtx_(new std::mutex), pCount_(new int(1)){}shared_ptr(const shared_ptr<T>& sp):ptr_(sp.ptr_), pmtx_(sp.pmtx_), pCount_(sp.pCount_){AddRef();}~shared_ptr(){Release();}private:void AddRef(){std::lock_guard<std::mutex> lock(*pmtx_);  // 自动管理锁的释放(*pCount_)++;}void Release(){std::lock_guard<std::mutex> lock(*pmtx_);if (--(*pCount_) == 0) {std::cout << "delete" << std::endl;delete ptr_;delete pCount_;delete pmtx_;}}private:T* ptr_;std::mutex *pmtx_;int* pCount_;};

但是, shared_ptr 还有一点不妥,当两个或以上的 shared_ptr 相互指向时,引用计数不会减为0,所以 C++11 还引入了 弱指针 weak_ptr 的概念,使用 weak_ptr 指向 shared_ptr 管理的对象不会增加 shared_ptr 的引用计数。

shared_ptr 可以隐式类型转换为 weak_ptr ,weak_ptr 可以通过它的成员函数 lock() 转化为 weak_ptr ,示例:

std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp;// shared_ptr 可以直接隐式类型转化为 weak_ptrif (std::shared_ptr<int> locked_sp = wp.lock())// weak_ptr通过使用lock转化为shared_ptr
{// 成功获取 shared_ptr,安全使用对象std::cout << *locked_sp << std::endl;
}
else
{// 对象已经被销毁std::cout << "The object is no longer available." << std::endl;
}

http://www.mrgr.cn/news/24403.html

相关文章:

  • Go 语言中SplitByChars 方法
  • 下班后做小红书第7个月,涨粉7w,累计变现5w+,我只用到五个点
  • 拒稿后另投他刊,仍旧被判定“一稿多投”?
  • 【路径规划】APF算法、Vortex APF算法、Safe APF算法和动态Windows方法的比较
  • 让AI成为打光工具人(Stable Diffusion进阶篇:Imposing Consistent Light)
  • WinRAR下载安装完整教程
  • 微信小程序接收蓝牙数据超过20字节断包解决方案
  • 77-java 装饰器模式和适配器模式区别
  • 暴雨液冷服务器硬刚液冷放量元年
  • 平价蓝牙耳机哪个牌子好?四款宝藏机型独家推荐
  • String 的 replace replaceAll 方法 —— 将字符串中所有中文逗号替换为英文逗号
  • 点亮第一盏LED灯,认识stm32最小系统板
  • “版权护航·星影计划”暨电影《末代天师》发布仪式
  • NVIDIA RAG小实验 一
  • C语言补习课——文件篇
  • 视频编辑SDK解决方案,代码逻辑结构清晰,接入便捷
  • WebRTC服务器搭建
  • 为什么Node.js不适合CPU密集型应用?
  • 运动耳机选哪种好用?六条绝妙选购要点避免踩坑
  • 如何防止图纸外泄?图纸防泄密的六个方法(必备清单!)