C++小知识——内存管理之智能指针

在这里插入图片描述
在C++中,动态内存的管理是通过一对运算符来完成的:
1)new:在动态内存中为对象分配空间并返回一个指向该对象的指针
2)delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存
因为这样的原因,我们时常很难确保在正确的时间去释放内存,甚至我们忘记了去释放内存,这样就会产生内存泄漏,有时在尚有指针引用内存时,我们提前释放了它,那么这个时候就会产生引用非法内存的指针。

引言

为了更容易、更安全地使用动态内存,新标准库提供了智能指针类型来管理动态对象,均定义在memory头文件中。

一、shared_ptr共享智能指针

std::shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存,资源被几个指针共享(可以通过user_count()来查看资源的所有者个数),在最后一个shared_ptr析构时内存才会释放

1.1 基本使用

1.1.1 初始化方法

1)调用make_shared标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr
2)通过new来构造

1.1.2 shared_ptr的拷贝和赋值

当进行拷贝或赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象。
1)递增情况:
无论何时我们去拷贝一个shared_ptr,计数器都会递增

  • 用一个shared_ptr去初始化另一个shared_ptr
  • 将它作为参数传递给一个函数时
  • 作为函数的返回值时

2)递减情况:

  • 给shared_ptr赋予一个新的值的时候
  • shared_ptr被销毁(例如一个局部的shared_ptr离开其作用域)的时候
    一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

1.2 举个栗子

在这里插入图片描述
在都reset之后,引用计数均变为0.

1.3 其他功能

通过get方法来获取原指针,但不能释放

std::shared_ptr<int> ptr(new int(10));
int *p = ptr.get();
delete p;		// error

指定删除器,在指针引用为0的时候自动调用。支持普通函数和lambda表达式

// 普通函数
void DeleteIntPtr(int *p) {delete p;}
shared_ptr<int> p(new int(10), DeleteIntPtr);

// lambda表达式
shared_ptr<int> p(new int(10), [](int *p) {delete p;});

当智能指针管理动态数组的时候,默认的删除器不支持数组对象。需要指定删除器,自定义删除器或者使用改善的默认修改器都可以。

shared_ptr<int> p(new int[10], [](int *p) {delete[] p;}); // lambda
shared_ptr<int> p1(new int[10], default_delete<int []>); // 指定delete []

1.4 使用时要注意的问题

1)避免一个原始指针初始化多个shared_ptr

int* p = new int;
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);

2)不要在参数实参中创建shared_ptr,而应该先创建,再用来做参数传递

func(shared_ptr<int>(new int), g());	// ERROR

shared_ptr<int> p(new int);
f(p, g());

二、unique_ptr独占智能指针

2.1 初始化

unique_ptr是一个独占型智能指针,与shared_ptr的不同:

  • 某个时刻智能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁
  • 没有类似make_shared的标准库函数返回一个unique_ptr。
  • 定义一个unique_ptr时,需要将其绑定到一个new返回的指针上。

几个要注意的:

- 不允许其他的智能指针共享其内部的指针
- 不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,即无法使两个unique_ptr指向同一个对象
- 用move()函数把unique_ptr传入来返回给其它的unique_ptr,转移之后,不再对之前的指针具有所有权,即原先的unique_ptr已经失效了
- 初始化unique_ptr须采用直接初始化的形式,如unique_ptr p2(new int(42));`// p2指向一个值为42的对象
- 虽然不能拷贝或赋值unique_ptr,但可以通过release或reset将指针的所有权从一个unique_ptr转到另一个unique_ptr,release成员返回unique_ptr当前保存的指针并将其置为空

2.2 举个栗子

在这里插入图片描述

2.3 其他功能

与shared_ptr不同,在指定删除器时,必须指定删除器类型

shared_ptr<int> ptr(new int(1), [](int *p){delete p;});           //ok

unique_ptr<int> ptr2(new int(1), [](int *p){delete p;});           //error
unique_ptr<int, void(*)(int *)> ptr2(new int(1), [](int *p){delete p;}); //ok

与shared_ptr不同,可以指向一个数组

unique_ptr<int []> ptr(new int[10]);   //ok
ptr[1] = 10;

shared_ptr<int []> ptr2(new int[10]); //error

三、weak_ptr弱引用智能指针

weak_ptr弱引用智能指针是用来解决相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr

3.1 基本使用

观察计数,通过use_count方法来获得当前资源的引用计数

std::shared_ptr<int> sp(new int(10));

std::weak_ptr<int> wp(sp);
std::cout << wp.use_count() << std::endl; // 输出1

std::shared_ptr<int> ssp(sp);
std::cout << ssp.use_count() << std::endl; // 输出2

观察是否有效

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);

if (wp.expired()) {
	cout << "sp 已经释放,无效" << endl;
} else {
	cout << "sp 有效" << endl;		// 输出
}

lock()函数,返回一个shared_ptr智能指针,获取所监视的shared_ptr

weak_ptr<int> gw;
auto spt = gw.lock();
cout << *spt << endl;

猜你喜欢

转载自blog.csdn.net/CSDN_dzh/article/details/83661105