【c++】智能指针以及实现
创始人
2025-05-30 18:02:59
0

1.auto_ptr(不要使用,auto_ptr是C++98的智能指针,C++11明确声明不再支持。)

最原始的智能指针。

auto_ptr具有以下缺陷:auto_ptr有拷贝语义,拷贝后源对象变得无效,这可能引发很严重的问题。由于 auto_ptr 基于【排他所有权模式】,这意味着:两个指针(同类型)不能指向同一个资源,复制或赋值都会改变资源的所有权。

复制auto_ptr对象时,把指针指传给复制出来的对象,原有对象的指针成员随后重置为nullptr。
使用auto_ptr要知道:
1. 智能指针不能共享指向对象的所有权
2. 智能指针不能指向数组。因为其实现中调用的是delete而非delete[]
3. 智能指针不能作为容器类的元素。


 2、unique_ptr(一种强引用指针)

它其实算是auto_ptr的翻版(都是独占资源的指针,内部实现也基本差不多).

但是unique_ptr的名字能更好的体现它的语义,而且在语法上比auto_ptr更安全(尝试复制unique_ptr时会编译期出错)

当你需要转移所有权,需要显式命令std::move,尽管转移所有权后 还是有可能出现原有指针调用(调用就崩溃)的情况。这个语法能强调你是在转移所有权,让你清晰的知道自己在做什么,从而不乱调用原有指针。 

void runGame(){std::unique_ptr b = new Base();std::unique_ptr b1 = b;//Error!编译期出错,不允许复制指针指向同一个资源。std::unique_ptr b2 = std::move(b);//转移所有权给b2.b->doSomething();//Oops!b指向nullptr,运行期崩溃
}

3、shared_ptr(一种强引用指针)
 

多个shared_ptr指向同一处资源,当所有shared_ptr都全部释放时,该处资源才释放。
每个shared_ptr都占指针的两倍空间,一个装着原始指针,一个装着计数区域(SharedPtrControlBlock)的指针

(用原始指针构造时,会new一个SharedPtrControlBlock出来作为计数存放的地方,然后用指针指向它,计数加减都通过SharedPtrControlBlock指针间接操作。)

//shared计数放在这个结构体里面,实际上结构体里还应该有另一个weak计数。下文介绍weak_ptr时会解释。
struct SharedPtrControlBlock{int shared_count;
};//大概长这个样子(化简版)
template
class shared_ptr{T* ptr;SharedPtrControlBlock* count;
};

 每次复制,多一个共享同处资源的shared_ptr时,计数+1。每次释放shared_ptr时,计数-1。
当shared计数为0时,则证明所有指向同一处资源的shared_ptr们全都释放了,则随即释放该资源(还会释放new出来的SharedPtrControlBlock)。

缺陷:模型循环依赖(互相引用或环引用)时,计数会不正常

//假如有这么一个怪物模型,它有2个亲人关系class Monster{std::shared_ptr m_father;std::shared_ptr m_son;
public:void setFather(std::shared_ptr& father);/void setSon(std::shared_ptr& son); ~Monster(){std::cout << "A monster die!";} 
};
//然后执行下面函数void runGame(){std::shared_ptr father = new Monster();std::shared_ptr son = new Monster();father->setSon(son);son->setFather(father);
}

猜猜执行完runGame()函数后,这对怪物父子能正确释放(发出死亡的悲鸣)吗?
答案是不能。
开始:
father,son指向的堆对象 shared计数都是为2,son智能指针退出栈:son指向的堆对象 计数减为1,father指向的堆对象 计数仍为2。father智能指针退出栈:father指向的堆对象 计数减为1 , son指向的堆对象 计数仍为1。

函数结束:所有计数都没有变0,也就是说中途没有释放任何堆对象。为了解决这一缺陷的存在,出现弱引用指针weak_ptr。

4、weak_ptr(一种弱引用指针)
 

weak_ptr只有某个对象的访问权,而没有它的生命控制权 即是 弱引用,所以weak_ptr是一种弱引用型指针,不控制所指向对象生存期的智能指针,它指向一个由shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。)

主要是为了解决两个问题:一是循环引用问题,使得资源无法释放;例如 A 对象含有一个 shared_ptr,而 B 对象也含有一个 shared_ptr,那么很容易产生循环引用,使得内存无法释放。

悬挂指针(dangling pointer):指针指向的内存被删除;一个简单的场景是,A 线程创建资源,并传递给 B 线程,B 线程只读访问资源;但是 A 线程随后可能释放了资源,B 没有感知,而得到了一个悬挂指针。)

内部实现:
计数区域(SharedPtrControlBlock)结构体引进新的int变量weak_count,来作为弱引用计数。
每个weak_ptr都占指针的两倍空间,一个装着原始指针,一个装着计数区域的指针(和shared_ptr一样的成员)。
weak_ptr可以由一个shared_ptr或者另一个weak_ptr构造。
weak_ptr的构造和析构不会引起shared_count的增加或减少,只会引起weak_count的增加或减少。

被管理资源的释放只取决于shared计数,当shared计数为0,才会释放被管理资源,也就是说weak_ptr不控制资源的生命周期。

但是计数区域的释放却取决于shared计数和weak计数,当两者均为0时,才会释放计数区域。

//shared引用计数和weak引用计数
//之前的计数区域实际最终应该长这个样子
struct SharedPtrControlBlock{int shared_count;int weak_count;
};//大概长这个样子(化简版)
template
class weak_ptr{T* ptr;SharedPtrControlBlock* count;
};

针对空悬指针问题:
空悬指针问题是指:无法知道指针指向的堆内存是否已经释放。

得益于引入的weak_count,weak_ptr指针可以使计数区域的生命周期受weak_ptr控制,

从而能使weak_ptr获取 被管理资源的shared计数,从而判断被管理对象是否已被释放。(可以实时动态地知道指向的对象是否被释放,从而有效解决空悬指针问题)

如果对象存在,lock()函数返回一个指向共享对象的shared_ptr(引用计数会增1),否则返回一个空shared_ptr。weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。

由于weak_ptr并没有重载operator ->和operator *操作符,因此不可直接通过weak_ptr使用对象,同时也没有提供get函数直接获取裸指针。典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
 

#include 
#include 
#include 
class T{public:T(int id): m_id(id){}int showid(){return m_id;}private:int m_id;
};void threadtest(std::weak_str t){std::this_thread::sleep_for(std::chrono::seconds(1));std::shared_ptr sp = t.lock();if(sp)std::cout<showID();                      
}
int main()
{std::shared_ptr sp = std::make_shared(1);std::thread t2(threadtest, sp);t2.join();return 0;
}


 

上一篇:23春-第三次集训题解

下一篇:smtp 抓包

相关内容

热门资讯

【MySQL】锁 锁 文章目录锁全局锁表级锁表锁元数据锁(MDL)意向锁AUTO-INC锁...
【内网安全】 隧道搭建穿透上线... 文章目录内网穿透-Ngrok-入门-上线1、服务端配置:2、客户端连接服务端ÿ...
GCN的几种模型复现笔记 引言 本篇笔记紧接上文,主要是上一篇看写了快2w字,再去接入代码感觉有点...
数据分页展示逻辑 import java.util.Arrays;import java.util.List;impo...
Redis为什么选择单线程?R... 目录专栏导读一、Redis版本迭代二、Redis4.0之前为什么一直采用单线程?三、R...
【已解决】ERROR: Cou... 正确指令: pip install pyyaml
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
Lock 接口解读 前置知识点Synchronized synchronized 是 Java 中的关键字,...
Win7 专业版安装中文包、汉... 参考资料:http://www.metsky.com/archives/350.htm...
3 ROS1通讯编程提高(1) 3 ROS1通讯编程提高3.1 使用VS Code编译ROS13.1.1 VS Code的安装和配置...
大模型未来趋势 大模型是人工智能领域的重要发展趋势之一,未来有着广阔的应用前景和发展空间。以下是大模型未来的趋势和展...
python实战应用讲解-【n... 目录 如何在Python中计算残余的平方和 方法1:使用其Base公式 方法2:使用statsmod...
学习u-boot 需要了解的m... 一、常用函数 1. origin 函数 origin 函数的返回值就是变量来源。使用格式如下...
常用python爬虫库介绍与简... 通用 urllib -网络库(stdlib)。 requests -网络库。 grab – 网络库&...
药品批准文号查询|药融云-中国... 药品批文是国家食品药品监督管理局(NMPA)对药品的审评和批准的证明文件...
【2023-03-22】SRS... 【2023-03-22】SRS推流搭配FFmpeg实现目标检测 说明: 外侧测试使用SRS播放器测...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
初级算法-哈希表 主要记录算法和数据结构学习笔记,新的一年更上一层楼! 初级算法-哈希表...
进程间通信【Linux】 1. 进程间通信 1.1 什么是进程间通信 在 Linux 系统中,进程间通信...
【Docker】P3 Dock... Docker数据卷、宿主机与挂载数据卷的概念及作用挂载宿主机配置数据卷挂载操作示例一个容器挂载多个目...