面试的时候被问到智能指针,虽然回答出来了,但是很多细节没有讲清楚,决定看看资料,重新学一下C++智能指针。
智能指针相关面试题
关于智能指针整理的面试题:
- 什么时候用裸指针比智能指针更好:
- 在函数传递的时候,不要传递shared_ptr本身,而是用原始指针。因为会有性能损失,原子操作的自增自减等。
- 智能指针shared_ptr线程安全吗
- shared_ptr引用计数的实现了解吗
- 多线程下shared_ptr会被析构两次吗
- unique_ptr在项目哪里用到
- weak_ptr了解吗
- weak_ptr的expired是线程安全嘛?
- shared_ptr和weak_ptr如何配合使用?
智能指针的基本介绍
三种智能指针的用法
- shared_ptr:允许多个指针指向一个对象
- unique_ptr:独占的指向所指对象。unique_ptr 更轻量,所以尽量使用unique_ptr。
- weak_ptr:弱引用,指向shared_ptr所管理的对象
智能指针主要用于管理在堆上分配的内存(即管理分配在堆上的对象),但是智能指针本身存在栈上
在初始化一个智能指针的时候,用普通指针或者new出来的地址初始化是没有问题的。
但是如果指向某个内存Ox00000001的智能指针ptr1已经存在,同时还有有个一个普通指针p1指向,当你用普通指针p1(或者ptr1.get())初始化另一个智能指针ptr2时,就有问题了:ptr1和ptr2的引用计数都会是1,他们是分别计数的,当某个智能指针离开其作用域,会导致另一个智能指针也失效。
总结来说就是:
- 在某个内存第一次被智能指针初始化的时候,用普通指针或者new出来的地址初始化是没有问题的。
- 但是当指向某个内存已经有智能指针指向时,只能用该智能指针初始化。
3. 线程安全
3.1. shared_ptr线程安全的讨论
shared_ptr的线程安全隐患
主要是以下几个方面:
(1) 引用计数的加减操作是线程安全。
(2) 修改shared_ptr指向(写操作) 不是线程安全。
3.1.1 引用计数的线程安全
shared_ptr中除了有一个指针,指向所管理数据的地址。还有一个指针执行一个控制块的地址,里面存放了所管理数据的数量(常说的引用计数)、weak_ptr的数量、删除器、分配器等。
也就是说对于引用计数这一变量的存储,是在堆上的,多个shared_ptr的对象都指向同一个堆地址。在多线程环境下,管理同一个数据的shared_ptr在进行计数的增加或减少的时候是线程安全的吗?
答案是肯定的,这一操作是原子操作(不会被线程调度机制打断的操作)。
结论:共享引用计数的不同的shared_ptr被多个线程写,是线程安全的。
3.1.2 对shared_ptr写操作的线程安全
场景一:同一个shared_ptr被多个线程写
先给出结论:此时不是线程安全的。
首先回顾一下shared_ptr指针的内部结构,包含两个重要的变量:ptr指针和引用计数指针。
对于shared_ptr的复制则分为两个步骤:
- 复制ptr指针
- 复制引用计数指针
所以,由于 shared_ptr 有两个数据成员,读写操作不能原子化,因此不少线程安全的。
可以参考例子:
陈硕:智能指针的探讨
年年年年:智能指针与线程安全
场景二:同一个shared_ptr被多个线程读;
仅仅是读取,而未修改数据所以给出结论:
同一个shared_ptr被多个线程“读”是安全的;
3.1.3 shared_ptr线程安全的总结
shared_ptr的线程安全总结如下。(shared_ptr)的引用计数由于本身是原子操作所以是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:
- 同一个shared_ptr被多个线程“读”是安全的;
- 同一个shared_ptr被多个线程“写”是不安全的;
- 共享引用计数的不同的shared_ptr被多个线程”写“ 是安全的
请注意,以上是 shared_ptr 对象本身的线程安全级别,不是它管理的对象的线程安全级别。