(十一)C++的四种智能指针

C++的标准模板库STL一共提供了四种智能指针,分别是auto_ptr、unique_ptr、shared_ptr、weak_ptr。它们的作用就是提我们管理动态内存分配。说简单点,就是怕你new/new[]了新的对象,然后忘记释放或者程序出现问题提前终止导致未运行到delete/delete[],造成内存泄露。

所以如果只能能自己释放内存就好了。。。这就是智能指针存在的意义。智能指针实际是个类对象,它管理一个指针(真正意义上的指针),在类对象失效(超出作用范围)时,调用类对象的析构函数时,自动释放它指向的对象。就和我们处理类的时候一样:
构建类,传入指针,析构类,delete指针。

1. auto_ptr指针

起初C++提供的是auto_ptr,但是这个不好用,最大的点就是这玩意可以完成相互赋值并且编译期不报错!!!

#include <iostream>
#include <string>
#include <memory>

int main()
{
	std::auto_ptr<std::string> ptr1(new std::string("string"));

	std::string *p = new std::string("123");
	std::auto_ptr<std::string> ptr2(p);

	ptr2 = ptr1;
	std::cout << *ptr1;
}

这段代码是可以编译的,并且编译正确,但是运行到ptr1的输出时,程序崩溃。为啥呢?因为ptr2 = ptr1,ptr1指向的对象现在交给ptr2管理了,那么ptr1指向空值。
在这里插入图片描述
我当然可以在使用ptr1之前进行get判断

if (!ptr1.get())
	{
		std::cout << "ptr1 is empty";
	}
	else
	{
		std::cout << *ptr1;
	}

在这里插入图片描述
但是如果能不判断不就更好么,编译器在编辑期间就发现错误,及时提醒我有异常。这就是C++11引用新的智能指针的原因,C++11还保留着auto_ptr,为了可以更好的兼容,但是C++17已经删除了auto_ptr。

2. unique_ptr指针

我们来看下unique_ptr这个智能指针,它就可以解决auto_ptr的问题:

	std::unique_ptr<std::string> ptr1(new std::string("string"));

	std::string *p = new std::string("123");
	std::unique_ptr<std::string> ptr2(p);

	ptr2 = ptr1;//这句话编译报错

这个时候,编译器就会报错:
“std::unique_ptr<std::string,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)”: 尝试引用已删除的函数

如果我真的想用ptr2=ptr1呢?C++提供了move方式:

	std::unique_ptr<std::string> ptr1(new std::string("string"));

	std::string *p = new std::string("123");
	std::unique_ptr<std::string> ptr2(p);

	ptr2 = move(ptr1);

但是我强烈要求你不要这么干,特别是实际开发中,那么多代码,你把ptr1的权限move给ptr2了,那么此时ptr1又指向了空,谁再次调用ptr1时会崩溃(实际开发代码太多,调试特别慢):

std::cout << *ptr1;

在这里插入图片描述
虽然说unique_ptr对对象是独占的,不允许两个unique_ptr指向同一个对象,但是它可以指向临时右值,啥意思呢,如果右值是一个临时的、很快就会被销毁的unique_ptr(你都没机会去用它),那么编译器是允许的!例如:

    std::unique_ptr<std::string> ptr3 = std::unique_ptr<std::string>(new std::string("test"));
	std::cout << *ptr3 << std::endl;

程序输出ptr3,右值std::unique_ptrstd::string(new std::string(“test”))也是一个unique_ptr,但是它是一个临时右值,构造函数创建的临时对象在所有权被转移给ptr3以后就会被销毁,所以这段代码是正常的。

3. shared_ptr指针

如果你需要指向同一个对象,强烈建议你使用shared_ptr,这个指针就是为了你可以使用多个智能指针指向同一个对象。它内部有一个计数器,每有一个指针指向同一个对象,那么这个计数器加1,每有一个指针被释放,计数器减1,如果计数器为0,shared_ptr就会释放指向的对象。shared_ptr可以用use_count来查看有多少指针引用:

	std::shared_ptr<std::string> ptr1(new std::string("string"));

	std::string *p = new std::string("123");
	std::shared_ptr<std::string> ptr2(p);

	ptr2 = ptr1;

	std::cout << ptr1.use_count() << std::endl;//引用数为2,ptr1和ptr2都指向了相同的对象string
	ptr1.reset();//如果reset未指向任何新的对象,那么ptr1将指向空指针
	std::cout << ptr2.use_count();//引用数为1,只有ptr2指向对象string
	std::cout << ptr1.use_count() << std::endl;//ptr1指向空指针,引用数为0
	std::cout << *ptr1 << std::endl;//会报错的,因为ptr1将指向空指针nullptr,这是测试,所以无所谓了,实际开发不要这么做

在这里插入图片描述

4. weak_ptr指针

weak_ptr弱指针,是配合shared_ptr使用的,最常见的错误就是两个shared_ptr指针相互引用,导致的无法释放,引起内存泄露(这样的例子网上很多):
class B包含class A类型shared_ptr,class A包含class B类型的shared_ptr,相互引用导致引用计数无法变为0

#include <iostream>
#include <memory>

class A;
class B;

class B {
private:
	std::shared_ptr<A> ptr;
public:
	void setPtr(std::shared_ptr<A> ptr) {
		this->ptr = ptr;
	}
	~B() {
	}
};

class A {
private:
	std::shared_ptr<B> ptr2;
public:
	void setPtr2(std::shared_ptr<B> ptr2) {
		this->ptr2 = ptr2;
	}
	~A() {
	}
};

int main() {
	{
		std::shared_ptr<B> b(new B);
		std::shared_ptr<A> a(new A);
		b->setPtr(a);
		a->setPtr2(b);

		std::cout << b.use_count() << std::endl; // 2
		std::cout << a.use_count() << std::endl; // 2
	}
	return 0;
}

在这里插入图片描述

如何选用智能指针呢?
1.需要多个指针指向同一个对象,使用shared_ptr
2.只需要一个指针指向一个对象对象,用unique_ptr
3.会发生shared_ptr 的循环引用问题,用weak_ptr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值