1. new和delete的配套使用
- delete和new应配套使用,new分配内存,应使用delete来释放;new []为数组分配内存,则应使用delete []来释放
- new和delete的配套使用可能出现的问题
忘记delete内存
同一块内存释放两次
使用已经释放的对象
2. 智能指针介绍
-
shared_ptr允许多个指针指向同一个对象;unique_ptr独占所指向的对象;weak_ptr弱引用,指向shared_ptr所指向对象
-
头文件
#include <memory>
2.1 auto_ptr
-
C98引入,C11中已弃用
-
auto_ptr存在的问题:当使用两个指针指向同一个对象时,程序可能会尝试删除同一个对象两次,即使在进行移动运算符重载(深拷贝)后,已释放对象所有权的指针可能再次访问该对象导致程序奔溃(编译没问题、运行时coredump)
-
auto_ptr成员函数
get() // 返回原始指针 reset() // 释放资源及所有权;p2(reset(p1))则p2会释放原来对象的所有权 release() // 释放对资源的所有权,析构函数没有调用
-
auto_ptr判断智能指针是否为空
if (p.get() == nullptr) {...}
-
访问指针的成员函数:.get() 或 .reset(),通过.访问
-
访问对象的成员函数:->func(),通过->访问
-
p2 = p1
p2会接管p1的内存管理权,p1会变成空指针,如果p2原来不为空,会释放原来的资源
2.2 unique_ptr
-
C11引入,用于取代auto_ptr,独享所有权的智能指针
-
unique_ptr如果尝试使用两个指针指向同一对象,在编译期就会报错
-
而且unique_ptr能和new/new[]一起使用,auto_ptr/shared_ptr只能使用new
-
无法使两个unique_ptr指向同一个对象。无法进行复制/构造复制赋值操作,可以进行移动构造和移动赋值操作。即不能使用p2=p1,可以使用p2=std::move(p1)
-
当unique_ptr被删除释放时,会释放它指向的对象
-
为动态申请的内存提供异常安全
-
可以将动态申请的内存所有权传递给函数,也可以从函数返回动态申请的内存所有权
-
可以在容器中保存指针
-
具有auto_ptr应有的功能
-
unique_ptr成员函数:
get() // 返回原始指针 reset() // 释放资源及所有权 release() // 释放对资源的所有权,析构函数没有调用 // release返回的指针通常被用来初始化另一个智能指针或赋值, // 如果赋值给普通指针程序要负责资源的释放,释放内存要使用reset swap(p1, p2) // 交换两个智能指针的值
-
unique_ptr的使用
if (p == nullptr) {} // 判断unique_ptr指针是否为空 unique_ptr不支持普通拷贝和赋值,但可以通过release和reset转移指针所有权 unique_ptr<string> p2(p1.release()); // 将所有权从p1转移给p2 unique_ptr<string> p3(new string("abcde")); p2.reset(p3.release()); // p3置空,并返回指针 // p2释放原来指向的对象,并重新指向p3对象 unique_ptr不支持=赋值,但作为函数返回值的时候使用了移动构造函数: unique_ptr<Test> fun() { return unique_ptr<Test>(new Test("789")); } unique_ptr<Test> p = func(); // 使用了移动构造函数
-
p2 = std::move(p1)
std::move(p1)构造了右值,p1的内存所有权转移给p2但是资源未释放。
如果p2原来不为空,会释放p2原来的资源。
2.3 shared_ptr
-
可以被多个指针共享,通过计数机制来表明资源被几个指针共享
-
可以使用=来赋值,引用次数变化
-
shared_ptr成员函数
get() // 返回原始指针 reset() // 释放资源及所有权 swap(p1, p2) // 交换两个智能指针 use_count() // 查看资源的所有者数量 // shared_ptr没有release()成员
-
reset()函数说明
p.reset() 若p是唯一指向对象的shared_ptr,则会释放此对象
p.reset(q) 若传递了参数内置指针q,会令p指向q,q释放对资源的所有权? -
如果p1.use_count()和p2.use_count()都是2;p2.reset()资源不会释放;p1.reset()资源释放
-
p2 = p1;
p1指向资源的引用次数+1,p2指向的资源引用次数-1
2.4 weak_ptr
-
weak_ptr是用来解决shared_ptr相互引用时的死锁问题
-
对象的弱引用,不会增加对象的引用计数
-
weak_ptr可以通过lock函数来获得shared_ptr
Class A {weak_ptr<B> pb;} 不能使用pa->pb->print()访问B类中成员函数, 可以使用shared_ptr<B> p = pa->pb.lock(); p->print()来进行访问
-
weak_ptr的常用方法
weak_ptr<T> wp; // 空weak_ptr指向类型为T的对象 weak_ptr<T> wp(sp); // 与shared_ptr sp指向相同对象 wp = p; // p可以是shared_ptr也可以是weak_ptr wp.reset(); // 将wp置为空 wp.use_count(); // 与wp共享对象的shared_ptr的个数 wp.expired(); // wp.use_count()==0为true,否则为false wp.lock(); // 返回指向wp对象的shared_ptr;如果expired返回true返回一个shared_ptr类型的空指针
2. 智能指针使用tips
-
智能指针的选择
1. 如果多个指针指向同一个对象,应选择shared_ptr 2. 如果不需要多个指针指向同一个对象,则可以使用unique_ptr 3. 如果使用new分配内存,并返回指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择 4. weak_ptr弱引用不增加对象引用计数,避免shared_ptr智能指针陷阱
-
创建智能指针时必须提供指针所指向的类型
-
默认初始化的智能指针中保存着一个空指针
-
shared_ptr和unique_ptr都支持的操作
shared_ptr<T> sp; unique_ptr<T> up; if (p == nullptr) *p获得所指向对象 p.get()返回p所保存的指针 swap(p1, p2); 或 p2.swap(p1)交换p1和p2的指针
shared_ptr独有的操作
make_shared<T> (args) 返回shared_ptr,指向类型为T的对象,args为初始化参数 shared_ptr<T> p(q); p是q的拷贝,此操作会增加q的计数 p = q 此操作会递减p的引用计数,递增q的引用计数,如果p引用次数减为0,则释放其管理的内存资源 p.use_count() 返回与p共享对象的智能指针数量,耗时,主要用于调试 p.unique() 若p.use_count() == 1返回true,否则返回false
make_shared函数头文件:#include<memory> shared_ptr<int> p1 = make_shared<int>(11); shared_ptr<string> p2 = make_shared<string>(5,'9'); shared_ptr<int> p3 = make_shared<int>(); // 不传递任何参数
shared_ptr与new的结合使用:不能显示转换,必须直接初始化 shared_ptr<int> p1 = new int(1024); // 错误 shared_ptr<int> p2(new int(1024)); // 正确,使用了直接初始化形式
-
不要混合使用普通指针和智能指针
-
std::move是转移对象的所有权
std::move可以移动右值,也可以把左值转换为右值,从而实现移动
可以用来转移unique_ptr的对象所有权
std::move要有左值承接才能调用移动构造函数 -
p2 = p1总结
auto_ptr: p2 = p1
p2会接管p1的内存管理权,p1会变成空指针,如果p2原来不为空,会释放原来的资源; 如果再使用p1访问对象会导致程序奔溃
unique_ptr: p2 = p1
编译报错。只允许一个指针指向对象;
unique_ptr: p2 = std::move(p1)
std::move(p1)构造了右值,p1的内存所有权转移给p2但是资源未释放;如果p2原来不为空,会释放p2原来的资源
shared_ptr: p2 = p1
p1指向资源的引用次数+1,p2指向的资源引用次数-1,如果减为0则释放资源
weak_ptr: p2 = p1
p1可以是shared_ptr也可以是weak_ptr,不会增加p1资源引用计数
参考文章:
智能指针实例详解
智能指针使用tips
C++ Prime Plus
created by shuaixio, 2022.02.20