智能指针之共享指针shared_ptr、弱指针weak_ptr、独占式指针unique_ptr

class MyClass
{
public:
	MyClass(int m = 0)
		:M(m){
		std::cout << "构造 " << M << std::endl;
	}
	MyClass(MyClass& myC)
		:M(myC.M){
		std::cout << "拷贝构造 " << M << std::endl;
	}
	~MyClass(){
		std::cout << "析构 " << M << std::endl;
	}
	const int get_Date()const{
		return this->M;
	}
private:
	int M;
};

void del_MyClass(MyClass* myC){
	std::cout << "自定义删除器 " << std::endl;
	return delete myC;
}

void del_Array_MyClass(MyClass* myC){
	std::cout << "自定义数组删除器 " << std::endl;
	return delete[] myC;
}

class Class_Delete
{
public:
	void operator()(MyClass* myC){
		std::cout << "自定义仿函数删除器 " << std::endl;
		return delete myC;
	}
};

class Class_Delete_A
{
public:
	void operator()(MyClass* myC) {
		std::cout << "自定义数组仿函数删除器 " << std::endl;
		return delete[] myC;
	}
};

一.共享指针

shared_ptr: public _Ptr_base<_Ty>
共享式智能指针:多个智能指针指向相同对象,该对象和其相关资源会在"最后一个reference被销毁"时被释放。
支持定制删除器,可防范Cross—DLL问题、自动解除互斥锁
1.创建的方式

	MyClass myC1(1), myC2(2), myC3(3);
	MyClass* myC_p = new MyClass(4);
	
	//方式一:默认、空的
	std::shared_ptr<MyClass> Str_ptr;
	//std::shared_ptr<MyClass> Str_ptr(nullptr);
	
	//方式二:make_shared传入对象
	//消除显示的new调用
	std::shared_ptr<MyClass> Str_ptr1 = std::make_shared<MyClass>(myC1);
	
	//方式三:直接传入一个对象的指针
	std::shared_ptr<MyClass> Str_ptr2(new MyClass(myC2));
	//std::shared_ptr<MyClass> Str_ptr2{ new MyClass(myC2) };
	//std::shared_ptr<MyClass> Str_ptr2(myC_p);
	
	//方式四:reset一个对象的指针
	std::shared_ptr<MyClass> Str_ptr3;Str_ptr3.reset(new MyClass(myC3));
	//Str_ptr3.reset();放弃之前对象所有权,并将智能指针初始化为空
	//Str_ptr3.reset(ptr);放弃之前对象所有权(默认删除器),并用ptr重新初始化Str_ptr3
	//reset()的第二参数为删除器,第三参数为分配器
	
	//方式五:创建与Str_ptr1共享的Str_ptr4智能指针,引用计数(use_count()返回值)增加
	std::shared_ptr<MyClass> Str_ptr4(Str_ptr1);
	//std::shared_ptr<MyClass> Str_ptr5(move(Str_ptr2));
	
	//方式六:从其他类型智能指针创建
	std::shared_ptr<MyClass> Str_ptr5(weak_ptr);		
	//std::shared_ptr<MyClass> Str_ptr5(move(unique_ptr));
	//std::shared_ptr<MyClass> Str_ptr5(move(auto_ptr));
	
	//方式七:别名构造
	std::shared_ptr<MyClass> Str_ptr6(Str_ptr1,myC_p);//myC_p需要手动释放
	//myC_p将赋值给成员element_type * _Ptr{nullptr};
	//Str_ptr1的成员_Rep将赋值给成员_Ref_count_base * _Rep{nullptr};

【注意】方式7很特殊。
一般std::shared_ptr Str_ptr()第二参数为删除器,第三参数为分配器
非常推荐使用方式二工厂函数make_shared,使得shared_ptr消除new和delete显示调用

2.共享指针的强制转换类型转换运算符

	1)static_pointer_cast
	2)dynamic_pointer_cast
	3)const_pointer_cast

3.线程安全接口

	bool atomic_is_lock_free(const shared_ptr<_Ty> * _Ptr);//如果shared_ptr<_Ty>的原子操作是无锁的,则返回true
	shared_ptr<_Ty> atomic_load(const shared_ptr<_Ty> * _Ptr);//返回_Ptr
	void atomic_store(shared_ptr<_Ty> * _Ptr, shared_ptr<_Ty> _Other);//原子地将_Other存储到*_Ptr
	shared_ptr<_Ty> atomic_exchange(shared_ptr<_Ty> * _Ptr, shared_ptr<_Ty> _Other);//将_Other复制到*_Ptr并原子地返回之前的值*_Ptr

4.可选自定义删除器(有默认删除器)、数组

	1)void del_MyClass(T* _myClass){ return delete _myClass; }
	std::shared_ptr<MyClass> Str_ptr4(new MyClass(4), del_MyClass);				//自定义
	或std::shared_ptr<MyClass> Str_ptr4(myC_p, std::default_delete<MyClass>());
	2)void del_Array_MyClass(MyClass* myC){ return delete[] myC; }				//自定义
	std::shared_ptr<MyClass> Str_ptr5(new MyClass[2], del_Array_MyClass);
	或std::shared_ptr<MyClass> Str_ptr5(new MyClass[2], std::default_delete<MyClass[]>());
	3)std::shared_ptr<MyClass> sh_ptr_a(new MyClass[2], Class_Delete_A());
	4)std::shared_ptr<MyClass[]> sh_ptr_a(new MyClass[2]);	//<>内为MyClass[]时,释放时能正确调用delete[]
	5)std::shared_ptr<MyClass> sh_ptr_a(new MyClass[2]);是错误的,因为默认删除器调用的是delete6)自定义删除器可以是函数、lambda表达式、仿函数(函数对象)。调用处也可以使用函数指针。
	7)针对数组不能使用*->原算符,要使用[]原算符,要使用运算符。
	8)需要释放除对象以外的相关资源,必须要自定义删除器。

5.错误使用
多个共享智能指针共享同一对象,但不能拥有同一个对象,否则会出现使用已析构的对象

	MyClass* myC_p = new MyClass(4);
	std::shared_ptr<MyClass> Str_ptr4(myC_p);
	std::shared_ptr<MyClass> Str_ptr5(myC_p);

6.共享指针存在问题
1)循环引用
2)明确想要共享但不拥有对象

7.共享指针的的应用场景
1)将容器作为shared_ptr的管理对象,如shared_ptr<list >,使得容器可以被安全的共享,其用法与普通的shared_ptr没有分别。
2)将shared_ptr作为容器的元素,如vector<shared_ptr >,因为shared_ptr支持拷贝语义和比较操作,符合容器对元素的要求,所以可以在容器中安全的容纳元素指针,而不是拷贝。相对于容纳原始指针,容器无发自动管理指针类型的元素,所以需要编写大量额外的代码来保证指针对应的内存正确的分配和释放。
3)用于桥接模式。shared_ptr是实现桥接模式的最佳工具,可以解决指针的共享问题和引用计数问题。
实现示例:

class sample
{
private:
	class impl;					//不完整类型内部声明
	shared_ptr<impl> p;			//shared_ptr成员变量
publicsample();
void print();
};
sample::sample():p(new impl){}
void sample::print()
{
p->print();
}

4)用于工厂模式
构造和析构都设置为保护或者私有,只允许工厂创建该的对象,create接口返回shared_ptr共享指针,create是外部唯一的创建接口,并且不允许外部delete来删除,交由shared_ptr共享指针管理释放。
5)定制删除其用于特殊资源管理工具

二.弱指针

weak_ptr: public _Ptr_base<_Ty>
弱指针是共享指针的辅助类,允许共享但不拥有某对象,不会关联对象的引用次数。
不能使用运算符*和->直接访问弱指针的引用对象,而是通过Lock函数生成对象的共享指针(可能为空)
当拥有该对象的最后一个共享指针失去其所有权时,任何弱指针都会自动变为空。
weak_ptr除default和copy构造函数外,只提供"接受一个shared_ptr"的构造函数。
1.可打破环状引用(循环引用)
1.确定弱指针指向的对象是否存在
1)调用use_count()来询问关联对象拥有的所有者数量。
2)调用expired(),等效于检查use_count()是否等于0。
3)使用共享指针构造函数将弱指针显示转换为共享指针。若无有效引用,此构造函数将引发bad_weak_ptr异常。

2.特殊成员函数
1)lock();尝试提升shared_ptr(如果对象还活着,可以提升为有效的shared_ptr,否则返回一个空的shared_ptr,提升失败。“提升/lock()”行为是线程安全的)。
2)运算符重载为类的成员函数,按实现分为针对智能指针的(=)、拥有的对象的(类的成员*,->,[]等,全局的==,<,!=等比较运算法)。
3)owner_before(const _Ptr_base<_Ty2>& _Right)//比较管理器对象(共享指针或弱指针)的地址

三.独占式指针

unique_ptr: public _Unique_ptr_base<_Ty, _Dx>
异常时可以帮助避免资源泄漏的智能指针。
采用独占式拥有,意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer拥有。
一旦拥有着被销毁或编程empty,或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应资源亦会被释放。
1.unique_ptr 用于取代 auto_ptr
1.检查独占式指针是否拥有对象
1)调用操作符bool()
2)与nullptr比较
3)获取原始指针再进行空判
2.独占式智能指针特性
1)独占式智能指针unique_ptr类无"拷贝构造"(右值引用可以)、无"=运算重载"(右值引用可以,nullptr可以),保证唯一性。
2)同一指针不能初始化两个独占式指针。
3)move()函数转交所有权,拥有新的对象的所有权会释放之前所有权的对象。
4)独占式指针作函数形参,调用该函数的独占式指针实参要使用move函数。 //性能低,无意义,直接使用一般类型
但是作为返回值却可以直接将该函数内创建的独占式指针直接返回。并在调用处直接用独占式指针接受。 //不推荐使用
3.异常时可以帮助避免资源泄漏的智能指针
#唯一指针有助于避免对象初始化期间引发的异常引起的资源泄漏
4.可选自定义删除器(有默认删除器)、数组
1)除使用自定义删除器与共享指针不同,其他一致,参照共享指针。
2)std::unique_ptr<MyClass, void()(MyClass)> un_ptr1(new MyClass(2), del_MyClass);
//std::unique_ptr<MyClass, Class_Delete> un_ptr2(new MyClass(2), Class_Delete());
std::unique_ptr<MyClass, Class_Delete> un_ptr2(new MyClass(2));
3)std::unique_ptr<MyClass[], void()(MyClass)> un_ptr_a1(new MyClass[3], del_Array_MyClass);
//std::unique_ptr<MyClass, Class_Delete_A> un_ptr_a2(new MyClass[2], Class_Delete_A());
//std::unique_ptr<MyClass[], Class_Delete_A> un_ptr_a2(new MyClass[2]);
【总结】:
unique_ptr设置自定义删除器需要在第二模板参数传入自定义删除器的类型。
自定义删除器为仿函数的时候,构造函数初始化参数"class()"可以不传,共享指针无第二模板参数,必须传入。
无设置自定义删除器时,第一模板参数"T或T[]"必须与构造函数第一参数"new或new[]"相对应;
在已设置自定义删除器时,则无需相对应,但是构造函数第一参数必须和自定义删除器"delete或delete[]"与之相对应。

四.自动指针

auto_ptr可以进行赋值拷贝,赋值拷贝后所有权转移。
unique_ptr无拷贝赋值语义,但实现了move语义。
auto_ptr对象不能管理数组(析构调用delete),而unique_ptr可以。
等瑕疵
被C++11弃用

如有错误或不足欢迎评论指出!创作不易,转载请注明出处。如有帮助,记得点赞关注哦(⊙o⊙)
更多内容请关注个人博客:https://blog.csdn.net/qq_43148810

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大胡子的艾娃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值