在C++中,动态内存的管理通过new 和 delete来进行。
但却经常存在以下三个常见问题:
- 忘记通过delete 释放内存,这在编程中经常发生,尤其在某个函数内申请的一块内存作为临时使用,但在函数结束后却没释放。这就会导致“内存泄露”问题。
- 使用已经释放过的内存,当有多个指针指向同一块内存时,某个指针释放后,却仍旧使用其他指针,就会导致这种错误。通过释放内存后把指针置为空,可以解决这种情况。
- 同一个块内存被释放多次,同样是多个指针指向同一块内存时,不当操作会导致这个情况。
所以在新的标准库里面,提供了两种智能指针shared_ptr类 和 unique_ptr类,均在 <memroy> 头文件
shared_ptr 类
允许多个指针指向同一个对象或内存,创建时需要指明类型
shared_ptr<int> p ;//一个空的shared_ptr,可以指向int
shared_ptr的操作 | 含义 |
---|---|
p | 可以当做判断是否为空,若p指向一个对象或内存,返回true。否则返回false |
*p | 获得p指向的对象的值 |
p.get() | 返回p中保存的指针(不要过多使用,当智能指针释放后,返回的这个指针指向的内存也会被释放) |
p.swap(q) | 交换p,q的指针 |
shared_ptr<T> p(q) | p 是 q(shared_ptr类型)的拷贝,二者会指向同一块内存 |
p.unique() | 若指向该内存的shared_ptr指针只有一个则返回true,否则返回false,即判断p.use_count()的值是否为1 |
p.use_count() | 返回与p共享对象的智能指针数量(注意只有智能指针),如是p.get()返回一个普通指针被拷贝,use_count不会增加,注意该函数会很慢 |
p.reset() | 如果p是唯一的指针,那么该函数会释放此对象,并置空。 |
p.reset(q) | q是一个普通指针,那么会释放原对象,把p指向q |
p.reset(q,d) | d是一个删除器,可以是一个lambda表达式同上,释放对象时是调用d而不是delete |
特点:
每个shared_ptr都关联一个计数器,通常称为引用计数。每当拷贝一个shared_ptr计数器都会增加。每当销毁一个共享的shared_ptr,计数器就会减小。当计数器减为0,他就会自动释放自己所指向的对象或内存。
释放内存的工作是交给析构函数来完成的。
shared_ptr和new的结合
shared_ptr可以通过new来分配内存,但不能将一个内置指针转化为一个只能指针,必须使用直接初始化的形式。如下
shared_ptr<int> p(new int(3);//这里p指向一个值为3的int
shared_ptr<int> p = new int(3);//注意,这里会报错。必须直接初始化
报错的原因是,new 返回的是一个int*。
make_shared 函数
最安全的分配和使用动态内存的方法,就是这个make_shared 标准库函数
该函数在动态内存中分配一个对象并初始化它,返回该对象的shared_ptr。该函数也在
<memory>中。使用make_shared也要求指定创建的对象类型,与模板类相似.
shared_ptr<int> p1 = make_shared<int>(3);//p1指向一个值为3的int
shared_ptr<string> p2 = make_shared<string>(3,'9');//p2指向一个"999"的string
shared_ptr<int> p3 = make_shared<int>();//p3指向一个值为0的初始化的int.
shared_ptr的拷贝和赋值
shared_ptr<int> p = make_shared<int>(3);
shared_ptr<int> q(p);//q也指向了这块内存,计数器会加一
shared_ptr<int> r = p;//r也指向了这块内存,计数器再加一
切记不要混用普通指针和智能指针。
如果使用普通指针通过.get()和智能指针指向同一个块地址,智能指针的计数器是不会增加的。当智能指针释放内存后。普通指针就会变成悬空指针(指向的内存被释放)如果再被使用,就会不安全且不可靠。
unique_ptr 类
unique_ptr会“独享”它所指向的对象,任何时刻都时只能有一个unique_ptr指向对象。unique_ptr被销毁时,它所指向的对象也会被销毁。
与shared_ptr不同,没有对应的类似make_shared的标准函数返回一个unqiue_ptr,所以必须使用new 返回的指针来进行初始化。即unique_ptr必须使用直接初始化形式。
unique_ptr<int> p1;//可以指向一个int的unique_ptr
unique_ptr<int> p2(new int(3));//指向一个值为3的int
unique_ptr<int> p3(p2);//报错,无法拷贝
unique_ptr<int> p3 = p2;//报错,unique_ptr不支持赋值,包括其他类型的地址也无法直接赋值
操作 | 含义 |
---|---|
unique_ptr<T> u | 空的unique_ptr,可以指向T类型的对象 |
u.release() | 放弃对指针的控制,返回指针,并把u置空 |
u.reset() | 释放u所指向的对象 |
u.reset(q) | 如果q时一个指针,那么u指向这个对象 |
通过releae或reset可以实现指针的转移。
unique_ptr<int> u1(new int(3));
unique_ptr<int> u2(u1.release());//u2指向这个值为3的int。u1会置空.
unique_ptr<int> u3(new int(2));
u3.reset(u2);//u3会释放原内存,现在指向值为3的int
参看书籍:
- 《C++ primer》 第五版