C++智能指针简介

一、为什么需要智能指针

C++ 的智能指针是一种用于自动管理动态内存的工具,可以避免开发者手动管理内存,减少内存泄漏和悬挂指针等问题。C++98里面引入了auto_ptr,C++11里面引入了shared_ptr、unique_ptr和weak_ptr,并且废弃了auto_ptr。

二、std::shared_ptr

std::shared_ptr 是一种共享所有权的智能指针,允许多个 shared_ptr 指向同一个对象。它通过引用计数来管理对象的生命周期。每个 shared_ptr 都有一个内部的引用计数器。当一个新的 shared_ptr 拷贝或赋值给其他 shared_ptr 时,引用计数增加;当一个 shared_ptr 被销毁时,引用计数减少。当引用计数降为零时,对象会被自动销毁。shared_ptr 的引用计数操作是线程安全的。

  • std::shared_ptr的内存模型如下:
    在这里插入图片描述

shared_ptr 内部包含两个指针,一个指向对象,另一个指向控制块(control block),控制块中包含一个引用计数(reference count), 一个弱计数(weak count)和其它一些数据。

  • std::shared_ptr的使用方法

#include <iostream>
#include <memory>

/* 常用函数方法 */
get()	// 返回shared_str中保存的裸指针
reset()	// 重置shared_ptr,如果带参数则将指针指向参数对象,否则置空
use_count()	// 返回shared_ptr的强引用计数
unique()	// 若use_count返回值是1则返回true,否则返回false

/* 初始化 */
std::shared_ptr<int> p1(new int(100));
std::shared_ptr<int> p2 = p1;
std::share_ptr<int> p3;
p3.reset(new int(200));
if (p3) {
	cout << "p3 is not null." << endl;
}
std::shared_ptr<int> p4 = std::make_shared<int>(300);

/* 指定删除器 
   当shared_ptr管理的对象是非new对象或者没有析构函数的类可以指定删除器
   删除器可以写成函数或者lambda表达式
*/
void DeleterIntPtr(int *p) {
	delete p;
}
std::shared_ptr<int> p1(new int(100), DeleterIntPtr);
std::shared_ptr<int> p1(new int(100), [](int *p) {delete p;});

/* 使用过程中注意事项 */
// 1、错误,不能将原始指针直接赋值给智能指针
std::shared_ptr<int> p1 = new int(100);

// 2、管理动态数组时必须指定删除器,因为默认删除器不支持删除动态数组
std::shared_ptr<int> p2(new int[100], [](int *p) {delete [] p;});

// 3、不要用原始指针初始化多个shared_pt,因为对象会被析构多次
int *ptr = new int;
std:shared_ptr<int> p1(ptr);
std:shared_ptr<int> p2(ptr); 

// 4、不要在函数实参中创建shared_ptr,因为函数参数计算顺序不同可能造成内存泄漏
function (shared_ptr<int>(new int), g());	// 可能导致int内存泄漏

// 5、不要将this指针作为shared_ptr来返回,因为this指针本身是个裸指针,可能导致类似3一样的错误
class A {
public:
	shared_ptr<A> GetSelf() {
		return shared_ptr<A)(this);	//错误
	}
	~A() {
		cout << "Destructor A" << endl;
	}
};
std::shared_ptr<A> p1(new A);
std::shared_ptr<A> p2 = p1->GetSelf();

class A: public std::enable_shared_from_this<A>{
public:
	shared_ptr<A> GetSelf() {
		return shared_from_this();	//正确
	}
	~A() {
		cout << "Destructor A" << endl;
	}
};
std::shared_ptr<A> p1(new A);
std::shared_ptr<A> p2 = p1->GetSelf();

// 6、不要造成循环引用
class A {
public:
	shared_ptr<B> bptr;
	~A() {
		cout << "Destructor A" << endl;
	}
};
class B {
public:
	shared_ptr<A> aptr;
	~B() {
		cout << "Destructor B" << endl;
	}
};
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
// 这种情况下ap和bp的引用计数都为2,离开作用域后引用计数各自减1,不会调用析构函数,造成内存泄漏

三、std::weak_ptr

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

  • std::weak_ptr的使用方法

#include <iostream>
#include <memory>

/* 常用函数方法 */
use_count()		// 获取引用计数
exired()		// 判断当前资源是否已经释放,返回bool值
lock() 			// 获取监视的shared_ptr,使用时相当于引用计数+1,离开作用域则-1

/* 初始化 */
shared_ptr<int> p1(new int(100));
weak_ptr<int> p2(p1);

/* 解决循环引用 */
class A {
public:
	shared_ptr<B> bptr;
	~A() {
		cout << "Destructor A" << endl;
	}
};
class B {
public:
	weak_ptr<A> aptr;
	~B() {
		cout << "Destructor B" << endl;
	}
};
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
// 只需将A或B的成员变量改为weak_ptr,这样ap的引用计数就是1,bp仍然是2,ap力扣作用域,引用计数-1为0导致A对象被析构,进而导致bptr引用计数-1为1,最后bp离开作用域引用计数继续-1为0,从而顺利被析构,无内存泄漏

四、std::unique_ptr

unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。它没有引用计数的开销,因此性能优越。

  • std::unique_ptr的使用方法

#include <iostream>
#include <memory>

/* 初始化 */
std::unique_ptr<T> p1(new T);
auto p2 = std::make_unique<T>();

/* 使用过程中注意事项 */
// 1、不能复制给另一个unique_ptr
std::unique_ptr<T> p1(new T);
std::unique_ptr<T> p2 = p1;		// 错误

// 2、可以通过std::move()函数来转让所有权
std::unique_ptr<T> p1(new T);
std::unique_ptr<T> p2 = std::move(p1);		// 正确

// 3、可以指向数组
std::unique_ptr<int []> p1(new int[10]);
p1[9] = 9;

// 4、指定删除器需指定删除器类型
std::shared_ptr<intvoid (*)(int *)> p1(new int(100), [](int *p) {delete p;});

五、总结

std::unique_ptr:独占所有权,不能复制,支持移动语义。
std::shared_ptr:共享所有权,引用计数管理生命周期,支持多次引用。
std::weak_ptr:不拥有对象,防止循环引用,弱引用。

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值