智能指针的用法shared_ptr


补充: new\delete和malloc\free
https://blog.csdn.net/A_With_better/article/details/123146285

智能指针

C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。程序员自己
管理堆内存可以提高了程序的效率,但是整体来说堆内存的管理是麻烦的,C++11中引入了智能指针的
概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常
时内存泄露等问题等,使用智能指针能更好的管理堆内存。

C++里面的四个智能指针: auto_ptr,** unique_ptr,shared_ptr, weak_ptr** 其中后三个是C++11支持,
并且第一个已经被C++11弃用。

shared_ptr共享的智能指针

std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。再最后一个shared_ptr析构的时候,内存才会被释放
shared_ptr共享被管理对象,同一时刻可以有多个shared_ptr拥有对象的所有权,当最后一个shared_ptr对象销毁时,被管理对象自动销毁

在这里插入图片描述

在这里插入图片描述

简单来说,shared_ptr实现包含了两部分:
第一部分:
一个指向堆上创建的对象的裸指针: raw_ptr
一个指向内部隐藏的、共享的管理对象:share_count_object

第二部分:
use_count,当前这个堆上对象被多少对象引用了,简单来说就是引用计数。
也可以用use_count()直接打印获取当前对象的引用数量.

shared_ptr的基本用法

1.初始化

通过构造函数、std::shared_ptr辅助函数和reset方法来初始化shared_ptr,代码如下:

// 智能指针初始化 
	std::shared_ptr<int> p1(new int(1)); 
	std::shared_ptr<int> p2 = p1; 
	std::shared_ptr<int> p3; 
	p3.reset(new int(1)); 
	if (p3) 
	{
		cout << "p3 is not null" << endl; 
	}
	cout << *p1 << endl << *p2 << endl << *p3 << endl;
	
//p3 is not null
//123
//123
//456

不能将一个原始指针直接赋值给一个智能指针,例如,下面这种方法是错误的:

std::shared_ptr<int> p = new int(1);  //报错

shared_ptr不能通过“直接将原始这种赋值”来初始化,需要通过构造函数和辅助方法来初始化。
对于一个未初始化的智能指针,可以通过reset方法来初始化,当智能指针有值的时候调用reset会引起
引用计数减1。
另外智能指针可以通过重载的bool类型操作符来判断。

reset成员函数

补充一下reset函数,上述案例中已经看到:
3种用法:

	//1.不带参数,置空
	shared_ptr<int>p1(new int(100));
	p1.reset();
	if (p1 == nullptr)
	{
		cout << "p1 was empty!" << endl;
	}
	//2.带参,指向新内存
	shared_ptr<int>p2(new int(200));
	p2.reset(new int(300));  //释放原内存(200),指向新内存(内存为300的)

	
	//3.空指针用reset来初始化
	shared_ptr<int>p3;
	p3.reset(new int(500));
	cout << *p3 << endl;  //500
make_shared

优先使用 make_shared 来构造智能指针,因为它更高效,安全
它能够在动态内存(堆)中分配并初始化一个对象,然后返回此对象shared_ptr.

auto sp1 = make_shared<int>(100); 

//相当于 
shared_ptr<int> sp1(new int(100));

但是使用make_shared方法生成shared_ptr对象,那就没有办法自定义删除器.

swap()函数

用于交换两个智能指针所指的对象.当然,因为是交换,所以引用计数并不发生变化:

	shared_ptr<string> ps1(new string("hello world!"));
	shared_ptr<string> ps2(new string("HELLO WORLD!"));
	std::swap(ps1, ps2);
	//ps1.swap(ps2);   //也可以这样用
	cout << *ps1 << endl;  //HELLO WORLD!
	cout << *ps2 << endl;  //hello world!
2. 获取原始指针(get())

当需要获取原始指针时,可以通过get方法来返回原始指针,代码如下所示:

	std::shared_ptr<int> ptr(new int(1));
	int *p = ptr.get();
	//delete p; 错误,这回导致一块内存delete两次

p.get()的返回值就相当于一个裸指针的值,不合适的使用这个值,上述陷阱的所有错误都有可能发生,遵守以下几个约定:

1.不要保存p.get()的返回值,无论是保存为裸指针还是shared_ptr都是错误的

2.保存为裸指针不知什么时候就会变成悬空指针,保存为shared_ptr则产生了独立指针

3.不要delete p.get()的返回值 ,会导致对一块内存delete两次的错误

3. 指定删除器

比如定义一个对象,想要自定义删除时,就会用到删除器.
如果用shared_ptr管理非new对象或是没有析构函数的类时,应当为其传递合适的删除器。

void DeleteIntPtr(int* p)
{
	cout << "call DeleteInterPtr" << endl;  
	delete p;
}
	//make_shared无法使用删除器
	shared_ptr <int> p1 = make_shared<int>(100);
	shared_ptr <int> p2(new int(1), DeleteIntPtr);

当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内存。

删除器可以是一个lambda表达式,上面的写法可以改为:

	std::shared_ptr<int> p(new int(1), [](int *p) {
	cout << "call lambda delete p" << endl;
	delete p;});

当我们用shared_ptr管理动态数组时,需要指定删除器,因为shared_ptr的默认删除器不支持数组对象,代码如下所示:

std::shared_ptr<int> p3(new int[10], [](int *p){
  delete [] p;});
使用shared_ptr要注意的问题
1.慎用智能指针

示例:

void proc(shared_ptr<int>ptr)
{
	return;
}

main中:

	int* p = new int(100);   //裸指针
	//proc(p);  //报错 int* p不能转换成shared_ptr<int>
	proc(shared_ptr<int>(p)); //参数是一个临时shared_ptr,用一个裸指针显式构造的
	*p = 45;   //不可预料的结果;因为p指向的内存已经被释放了

上述例子中,把一个普通指针绑到了一个shared_ptr上,那内存管理的责任就交给了只能指针shared_ptr来接管,在函数中已经被释放,这时候不应该在使用裸指针访问shared_ptr指向的内存了.

不要用一个原始指针初始化多个shared_ptr

例如下面错误范例:

	int *ptr = new int; 
	shared_ptr<int> p1(ptr); 
	shared_ptr<int> p2(ptr); // 逻辑错误

p1一个引用,p2一个引用,会导致p1,p2两个指针之间无关联关系,(每个指针的强引用计数都是1),所以释放ptr所指向的内存释放2次,显然会出问题

通过shared_from_this()返回this指针。

不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此,这样可能会导致重复析构,看下面的例子。

#include <iostream>

using namespace std;
class A
{
public:
	shared_ptr<A>  GetSelf() 
{ 
	return shared_ptr<A>(this); // 不要这么做 
}
~A()
{ 
	cout << "Deconstruction A" << endl; 
} 
};
int main() 
{ 
	shared_ptr<A> sp1(new A); 
	shared_ptr<A> sp2 = sp1->GetSelf(); 
	return 0; 
}

运行后调用了两次析构函数。
在这里插入图片描述

在这个例子中,由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。

正确返回this的shared_ptr的做法是:让目标类通过std::enable_shared_from_this类,然后使用基类的成员函数shared_from_this()来返回this的shared_ptr,如下所示。

class A : public std::enable_shared_from_this<A>
{
public:
	shared_ptr<A>GetSelf()
	{
		return shared_from_this();
	}
	~A()
	{
		cout << "deconstruction A" << endl;
	}
};

main

	shared_ptr<A> sp1(new A);
	shared_ptr<A> sp2 = sp1->GetSelf();
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值