常见的内存问题:
- 重复释放同一块内存会导致程序运行崩溃
- 没有及时释放不再使用的内存资源,造成内存泄漏,程序占用的内存资源越来越多
- 有些内存资源已经被释放,但指向它的指针并没有改变指向(成为了野指a
为什么要用智能指针方便和安全的使用动态内存智能指针的行为类似于常规指针
智能指针的作用:智能指针可以在适当时机自动释放分配的内存,可以很好地避免“忘记释放内存而导致内存泄漏”问题。
头文件:#include<memory>
- shared_ptr 允许多个指针指向同一个对象
- unique_ptr 独占这个对象
- weak_ptr 是一种弱引用指向shared_ptr所管理的对象
shared_ptr类:
shared_ptr类似vector,也是一个模板,创建的方式:
int a = 10;
//指向int 的shared_ptr
shared_ptr<int>p(&a);//指向a
//指向 int的vector 的shared_ptr
shared_ptr<vector<int>>p1;//默认初始化为nullptr
shared_ptr类的常用成员函数:
成员方法 | 功能 |
operator=() | 重载赋值号,可以相互赋值 |
operator*() | 获取shared_ptr所指的数据 |
operator->() | 获取内部成员,等价于(*p).men |
swap() | 交换两个相同类型 shared_ptr 智能指针的内容 |
reset() | 当函数没有实参时,该函数会使当前 shared_ptr 所指堆内存的引用计数减 1,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的 shared_ptr 对象会获得该存储空间的所有权,并且引用计数的初始值为 1。 |
get() | 获得 shared_ptr 对象内部包含的普通指针 |
use_count() | 返回同当前 shared_ptr 对象(包括它)指向相同的所有 shared_ptr 对象的数量 |
unique() | 判断当前 shared_ptr 对象指向的堆内存,是否不再有其它 shared_ptr 对象再指向它 |
make_shared<T>(args) | 返回一个shared_ptr,指向一个动态分配的类型为T的对象,使用args初始化此对象。 |
shared_ptr的创建的方式:
shared_ptr<int>b1;
shared_ptr<int>p;//默认初始化
shared_ptr<int>p1(b1);//传参
shared_ptr<int>p2(new int(42));//指向一个值为42的int
shared_ptr<int>p3 = make_shared<int>(10);//使用make_shared初始化
make_shared的使用:
shared_ptr的最安全的分配和使用动态内存的方法,make_shared会返回指向此对象的shared_ptr
shared_ptr<int>k1 = make_shared<int>(40);
shared_ptr<string>k2 = make_shared<string>(5, '3');//字符串可以这样使用 33333
shared_ptr<int>k3 = make_shared<int>();//默认为0
常用auto与make_shared一起使用
auto g1 = make_shared<int>(50);
auto b(g1);//shared_ptr的拷贝
当一个shared_ptr的计数器变为0时,它会自动释放自己管理的对象
auto g1 = make_shared<int>(50);
auto b(g1);//shared_ptr的赋值
auto kk = make_shared<int>(100);//kk指向的int只有一个引用者
kk = b;//给kk赋值,指向另一个内存,r原来指向对象的对象计数器-1==0,当计数器等于0时,自动释放内存
shared_ptr自动销毁所管理的对象:
当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr会调用析构函数来完成销毁。shared_ptr的析构函数会递减它所指向对象的引用计数,引用计数为0,析构函数就会销毁对象,并释放内存。注意:shared_ptr还会自动释放相关联的内存
不要把普通指针和智能指针混合使用:
智能指针的使用:
void text(shared_ptr<int>p)//值传递,会传递一个拷贝,拷贝时会增加引用计数
{
}
int main()
{
shared_ptr<int>p1(new int(20));//p指向一个数据,引用计数为1
text(p1);//拷贝时,引用计数+1,变为2
//执行完后引用计数-1,变为1
int i = *p1;//引用计数为1,没被销毁,正确
return 0;
}
以下为错误示范:
void text(shared_ptr<int>p)//值传递,会传递一个拷贝,拷贝时会增加引用计数
{
}
int main()
{
int* a(new int(20));//一个普通指针
text(shared_ptr<int>(a));//合法但内存会被释放
int j = *a;//a的内存被释放,a为空悬指针
cout << *a << endl;//该值为未定义
return 0;
}
不要用get函数去初始化另一个智能指针或为智能指针赋值
shared_ptr<int> p(new int(20));//引用计数为1
int* q = p.get();
{
shared_ptr<int>(q);//指向相同的内存
}
//程序块结束后,q被销毁,指向的内存被释放
int foo = *p;//未定义,p指向的内存被销毁
智能指针注意事项:
- 不使用相同的内置指针初始化或(reset)多个智能指针
- 不使用get()初始化或reset另一个指针指针
- 不delete get()返回的指针
- 使用了get()初始化的指针,当最后一个shared_ptr销毁时,指针变为无效
- 使用的智能指针管理的资源不是new分配的内存,需要传递一个删除器
unique_ptr
一个unique_ptr只拥有它所指向的一个对象
由于只能拥有一个对象,那么unique_ptr,不能使用普通的拷贝和赋值操作
在c++11种没有make_unique,来构造一个unique_ptr,但在c++14种新加了 make_unique
unique_ptr的创建:
unique_ptr<int>p(new int(20));
unique_ptr<int>p2();//空指针
unique_ptr<int>p3(nullptr);//空指针
无法赋值和拷贝:
unique_ptr<int>p(new int(20));
unique_ptr<int>p1(p);//报错
unique_ptr<int>p2;
p2 = p;//报错
函数 | 功能 |
unique_ptr<T> u1 | 空unique_ptr |
unique_ptr<T,D> u2 | u2会使用一个类型为D的对象来代替delete |
unique_ptr<T,D> u2(d) | 空unique_ptr,使用一个类型为D的对象来代替delete |
u=nullptr | 释放u指向的对象 |
u.release() | u放弃对指针的控制权,返回指针,并将u置为空 |
u.reset() | 释放u所指的对象 |
u.reset(q) | 提供了内置指针q,令u指向这个对象,否则将u置为空 |
unique_ptr不能拷贝和赋值,但可以把控制权转移:
unique_ptr<int>p(new int(20));
unique_ptr<int>p2(p.release());//将控制权转移给p2,把p置空
unique_ptr<int>p5(new int(30));
unique_ptr<int>p4(move(p5));//使用移动构造函数,p4获得p5的控制权,把p5置为空指针
传递unique_ptr参数和返回unique_ptr
不能拷贝unique_ptr,但可以拷贝或赋值一个将要销毁的unique_ptr
编译器对这种要返回的对象将要销毁,编译器会执行移动构造函数。
unique_ptr<int> text(int p)
{
return unique_ptr<int>(new int(p));//返回一个unique_ptr
}
unique_ptr<int> text2(int p)
{
unique_ptr<int>k(new int(p));
return k;//返回一个局部变量的拷贝
}
weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,指向由shared_ptr管理的对象。 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1,weak_ptr是一种“弱”共享对象。
成员函数 | 功能 |
weak_ptr<T> w | 空的weak_ptr可以指向类型为T的对象 |
weak_ptr<T> w(sp) | shared_ptr sp 指向相同对象的weak-ptr |
w=p | p可以是一个shared_ptrh或一个weak_ptr,赋值后共享对象 |
w.reset() | 将w置空 |
w.use_count() | 于w共享对象的shared_ptr的数量 |
w.expired() | w.use_count()为0返回true,否则返回false |
w.lock() | w.expired()为true,返回一个空的shared_ptr,否则返回w指向的对象 |
weak_ptr<T> 模板类中没有重载 * 和 -> 运算符,这也就意味着,weak_ptr 类型指针只能访问所指的堆内存,而无法修改它
weak_ptr指针的创建
weak_ptr<int>gg();//空的weak_ptr
shared_ptr<int>p(new int(5));
weak_ptr<int>k(p);//指向sharead_ptr对象,弱共享,引用计数不会改变
weak_ptr成员函数的使用
shared_ptr<int>p(new int(5));
weak_ptr<int>k(p);//指向sharead_ptr对象,弱共享,引用计数不会改变
shared_ptr<int>k1(k.lock());//k1和k共享对象
int i = k.use_count();//获取与k共享的shared_ptr的数量
k.reset();//将k置空