智能指针是c++很重要的一项技术,面试经常会问,诸如以下几个问题:
- shared_ptr的原理是什么?
- shared_ptr是线程安全的吗?
- 如何把指针赋给已存在的shared_ptr?
- 什么情况下使用unique_ptr?
- 如何将unique_ptr对象的所有权转移给另外一个unique_ptr?
- weak_ptr解决了什么样的问题?
智能指针管理的是动态内存的指针,是new出来的,那么什么情况下需要使用动态内存呢?
有以下几点:
- 程序不知道自己需要使用多少个对象
- 程序不知道所需对象的准确类型
- 程序需要在多个对象间共享数据
c++管理动态内存主要通过new和delete,实际开发中存在两种严重的问题:
- 忘记释放内存造内存泄漏
- 尚有指针引用内存的情况下就释放了它,造成非法引用。
使用智能指针能够有效解决以上问题。
- shared_ptr
- unique_ptr
- weak_ptr
三者的头文件是<memory>
一、shared_ptr
1.如何初始化?
shared_ptr<string> p;//空智能指针。
类似vector,智能指针也是模板,以上代码实际上是以默认初始化的方式,它保存着一个空指针。直接使用会出现段错误。
两种初始化方式:
<1>shared_ptr<string> sp1(new string("123"));//构造函数直接初始化
<2> shared_ptr<string> sp2 = make_shared<string>("123");
<3>shared_ptr<string>sp3(sp2);//这实际上时调用了拷贝构造,两个shared_ptr引用到同一个对象。
Note:
make_shared函数在动态内存中分配一个对象并初始化它,返回指向该对象的shared_ptr,它更加安全和稳定,自己new对象可能会抛异常。
在调用make_shared的时候传递的参数必须与该类的构造函数相匹配。
2.如何把一个已存在的动态内存指针赋给shared_ptr管理?
使用reset函数,代码如下:
shared_ptr<string> sp;
sp = new string("123");//错误,不能把一个指针赋值给shared_ptr
正确的做法是:
sp.reset(new string("123"));//这个在实际开发中,尤其重要。
3.智能指针较普通指针的优势?
实际开发中,难免会遇到异常,忘记delete,造成内存泄漏。
void fun(){
shared_ptr<int> sp(new int(10));
//在此处抛出异常,fun没有被捕获
}//函数结束时,shared_ptr依然能释放内存
void fun(){
int *p = new int(10);
//在此处抛出异常,fun没有被捕获
delete p;
}//函数结束时候,p指向的内存永远得不到释放了。
4.如何自定义删除器?
如果使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。
void myfree(void *p){
delete p;
}
int *x = new int(1);
shared_ptr<int> p(x,myfree); //自定义删除器
5.shared_ptr是线程安全的吗?
shared_ptr的引用计数本身是线程安全的,因为在发生拷贝、赋值、删除的时候,对引用计数明确。
shared_ptr本身不是引用计数类,它只是包含了一个指向引用计数类sp_counted_base的指针,sp_counted_base类才保存了引用计数,并且对引用计数字段提供无锁保。
6.shared_ptr相互引用的危害?
看一下以下代码:
class A{
public:
shared_ptr<B> pB;
};
class B{
public:
shared_ptr<A> pA;
};
int main(int argc,char *argv[]){
shared_ptr<A> spa(new A);
shared_ptr<B> spb(new B);
spa->pB = spa;
spb->pA = spb;
}
以上代码会出现相互引用,智能指针的引用计数永远到不了0,造成内存泄漏。
解决办法:创建对象的时候,shared_ptr持有它,当其他地方想使用这个对象的时候,应该持有该对象的弱智能指针(weak_ptr)。
7.使用shared_ptr有哪些注意点?
- 不使用相同的内置指针初始化或reset多个智能指针
- 不delete get()返回的指针
- 不使用get()初始化或者reset另外一个指针
- 如果使用智能指针管理的资源不是new分配的内存,记住传递它一个删除器
- 使用weak_ptr解决相互引用的问题。
二、unique_ptr
某一时刻只能有一个unique_ptr指向一个给定对象,也就是独占式的。
没有像make_shared类似的函数初始化,不支持拷贝和赋值。
虽然不能拷贝和赋值,但是可以通过release和reset来从一个非const unique_ptr转移给另外一个unique
使用示例
unique_ptr<string> p1(new string("text"));
unique_ptr<string>p2(p1.release());//从p1转移给p2,release将p1置于空
unique_ptr<string>p3(new string(text1));
p2.reset(p3.release());//reset释放了p2原来指向的内存
p2.release();//p2不会释放内存,而且丢失了指针
auto p = p2.release();//合法,但是要delete p
Note:
不能拷贝的规则有一个例外,那就是可以拷贝或赋值一个将被销毁的unique,比如从函数返回值。
未完...