C++智能指针基本使用

智能指针本质上是一个原始指针的包装,也是一个模板。调用new的时候,不需要调用delete,甚至不需要调用new

智能指针的区别在于管理底层指针的方式,shared_ptr允许多个指针指向同一个对象,unique_ptr独占所指的对象。还有一个weak_ptr的伴随类,是一种弱引用,指向shared_ptr所管理的对象

unique_ptr(作用域指针)

unique_ptr实际上并不是指针,而是一个对象。所以,不要企图对它调用 delete,它会自动管理初始化时的指针,在离开作用域时析构释放内存。也没有定义加减运算,不能随意移动指针地址。unique_ptr必须初始化,否则直接操作空指针会出错。为了避免这种错误,可以调用工厂函数 make_unique(),强制创建智能指针的时候必须初始化。

unique_ptr不能复制,也就是说两个unique_ptr不能指向相同的内存。因为如果两个指针指向同一块内存,但其中一个被释放了,那么另一个就指向了被释放的内存

#include <iostream>

class Entity
{
public:
	Entity()
	{
		std::cout << "created Entity" << std::endl;
	}
	~Entity()
	{
		std::cout << "destroyed Entity" << std::endl;
	}
	void print() { std::cout << "print entity" << std::endl; }
};

int main()
{
	{
		//ScopedPtr e = new Entity(); //隐式转换
		//std::unique_ptr<Entity> entity = new Entity(); //不能这样构造,因为unique_ptr使用了explicit,必须显式调用构造函数,没有隐式构造函数的转换
		//std::unique_ptr<Entity> entity (new Entity());
		std::unique_ptr<Entity> entity = std::make_unique<Entity>();//更推荐,主要原因是出于异常安全
		entity->print();
	}
	return 0;

因为unique_ptr不能复制,也就不能进行函数传参

unique_ptr的所有权:指针的所有权是“唯一”的,不允许共享,为了实现这个目的,unique_ptr 应用了 C++ 的“转移”(move)语义,同时禁止了拷贝赋值,所以,在向另一个 unique_ptr 赋值的时候,要特别留意,必须用 std::move() 函数显式地声明所有权转移。赋值操作之后,指针的所有权就被转走了,原来的 unique_ptr 变成了空指针,新的 unique_ptr 接替了管理权,保证所有权的唯一性

auto ptr1 = make_unique<int>(42);    // 工厂函数创建智能指针
assert(ptr1 && *ptr1 == 42);         // 此时智能指针有效

auto ptr2 = std::move(ptr1);         // 使用move()转移所有权
assert(!ptr1 && ptr2);               // ptr1变成了空指针

声明数组:

std::unique_ptr<int[]> ptr{new int[5] {1, 2, 3, 4, 5}}; 
//或者std::unique_ptr<int[]> ptr{std::make_unique<int[]>(5)};
for (int i = 0; i < 5; i++) {
	std::cout << ptr[i] << " ";
}

reset,get和release:

reset释放智能指针的内存,将内存还给操作系统,并将智能指针的地址重设为0

get返回原始指针

release将unique_ptr设为nullptr,但不会释放内存空间

std::unique_ptr<int[]> ptr{new int[5] {1, 2, 3, 4, 5}};
int* a = ptr.get();
std::cout << ptr << std::endl; //00CB0780
std::cout << a << std::endl;  //00CB0780
ptr.reset();
std::cout << ptr << std::endl; //00000000
std::cout << a << std::endl;  //00CB0780

shared_ptr(共享指针)

与 unique_ptr 的最大不同点:它的所有权是可以被安全共享的,也就是说支持拷贝赋值,允许被多个“人”同时持有,就像原始指针一样

shared_ptr的工作方式是引用计数(可以跟踪指针有多少个引用,如果发生拷贝赋值,也就是共享的时候,引用计数就增加,而发生析构销毁的时候,引用计数就减少,一旦引用计数达到0,就删除了),但引用计数的存储和管理都是成本(但成本较低)。

shared_ptr需要分配一块内存,叫控制块,用来存储引用计数。如果首先创建一个new Entity,然后将其传递给shared_ptr构造函数,它必须做两次分配,先做一次new Entity的分配,再做一次shared_ptr控制内存块的分配,使用make_shared能把它们组合起来,更有效。

int main()
{
	{
		std::shared_ptr<Entity> e0;
		{
			std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
			e0 = sharedEntity; //增加引用计数
		}//此时对象还没被销毁,因为引用计数还没达到0
	}
	return 0;
}

注意:make_shared不支持数组。通过use_count()可以获得当前有多少个引用计数

shared_ptr 的引用计数也导致了一个新的问题,就是“循环引用”,这在把 shared_ptr 作为类成员的时候最容易出现,典型的例子就是链表节点。weak_ptr专门为打破循环引用而设计,只观察指针,不会增加引用计数(弱引用),但在需要的时候,可以调用成员函数 lock(),获取 shared_ptr(强引用)。

可以把shared_ptr赋给weak_ptr,把shared_ptr赋给shared_ptr,会增加引用计数;而赋给weak_ptr不会增加引用计数。

std::weak_ptr<Entity> weakEntity = sharedEntity;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值