C++ 四种智能指针详解

  • 智能指针出现的原因:主要解决的是堆内存分配释放,忘记释放内存引发的内存泄漏问题,智能指针最主要的事情就是让程序员无需去注意内存释放,内存释放的问题放在智能指针内部进行处理。
  • 智能指针有四种,包括auto_ptr,shared_ptr,unique_ptr,weak_ptr,其中auto_ptr已经被C++11废弃,所以放在最后介绍。

unique_ptr


unique_ptr的几个重要特性

  • 1.两个unique_ptr不能指向一个对象,即unique_ptr不能共享它所管理的对象,这句话个人的理解的,unique_ptr无法赋值指针到另一个指针,让他们指向同一个对象。
    简单的示例代码:
class A{
public:
	A(){
		cout << "demo"<< endl;
	}
  void B(){
		cout << "测试" << endl;
	}
};

void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	unique_ptr<A> demo = A_ptr; //此时编译错误,无法通过赋值的方式传递指针地址,即与demo共享同一块内存
}

由上面代码可以看出,编译unique_ptr demo = A_ptr会报错,因为unique_ptr是只能单独享有对对象的独有权。
另外,unique_ptr无法通过赋值的形式创建内存对象,即:
C++ 四种智能指针详解_第1张图片
这种方式也是错误的。

  • 2.提前释放unique_ptr对象可以通过reset方法进行重置
    示例代码:
void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	A_ptr.reset();
	cout << A_ptr << endl;
}

运行结果:
C++ 四种智能指针详解_第2张图片
可以看出:
通过reset方法 可以直接删除原始指针,并且重置为空。

  • 3.我们可以通过转移所有权的方式,来使另一个指针拥有原先unique_ptr的地址
    示例代码:
void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	unique_ptr<A>BB = std::move(A_ptr);
	cout << A_ptr << endl;
	cout << BB << endl;
}

运行结果:

C++ 四种智能指针详解_第3张图片
通过std::move()方法,将A_ptr的对象转移到BB上面,A_ptr指针为空,BB拥有A_ptr指向的地址。

  • release()方法会释放原有的原始指针,但是不会删除原先的指针,删除原先的指针需要通过reset()方法
    示例代码:
void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	cout << A_ptr << endl;
	A * nn  = A_ptr.release();
	cout << nn << endl;
	cout << A_ptr << endl;
	delete nn;
	nn = nullptr;
	cout << nn << endl;
}

运行结果:
C++ 四种智能指针详解_第4张图片

  • 知识点:使用delete删除指针,编译器只会释放原先指向的内存空间,不会释放指针本身,所以需要通过设置指针为空来删除指针本身!!!
  • 4.使用get方法 获取关联的指针对象
void unique_ptr_use()
{
	unique_ptr<A> A_ptr(new A());
	A_ptr->B();
	A * m = A_ptr.get();
	cout << "11---" << m << endl;
	cout << A_ptr << endl;
	A * nn  = A_ptr.release();
	cout << nn << endl;
	cout << A_ptr << endl;
	delete nn;
	nn = nullptr;
	cout << nn << endl;
}

运行结果:
C++ 四种智能指针详解_第5张图片


shared_ptr


shared_ptr可以实现多个对象共同托管一个指针,这个是unique_ptr做不到的。当曾经的托管对象接触对其托管时,系统会自动执行delete p释放内存,这样保证了内存不会泄漏。

  • shared_ptr:可以查看赋值对象的个数,因为可以多个对象共同托管,所以个数需要获得,已经内置了一个use_count()方法返回当前可以操作该指针的对象个数。
  • 示例代码:
void shared_ptr_use()
{
	shared_ptr<A> mm = make_shared<A>();
	mm->B();
	cout <<"mm value:" << mm << endl;
	shared_ptr<A> ff(mm);
	cout << "ff value:" << ff << endl;
	cout << "个数为" << ff.use_count() << endl;
	mm.reset();//重置
	cout <<"now ff:"<< ff << endl;
	cout << "个数为" << ff.use_count() << endl;
	cout <<"now mm:"<< mm << endl;
}
  • 运行结果:
    C++ 四种智能指针详解_第6张图片

weak_ptr


  • 首先,weak_ptr不能直接定义一个对象类,需要配合shared_ptr进行使用,通过shared_ptr传指针地址给它。
  • weak_ptr主要解决的一个问题是shared_ptr相互引用无法释放内存的问题。
  • 实例代码:
class B;
class A{
public:
	A(){
		cout << "A init!" << endl;
	}
	~A(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<B>mm)
	{
		m_b = mm;
	}
private:
	shared_ptr<B> m_b;
};

class B{
public:
	B(){
		cout << "A init!" << endl;
	}
	~B(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<A>mm)
	{
		m_a = mm;
	}
private:
	shared_ptr<A> m_a;
};

void test_ptr()
{
	shared_ptr<A>a_ptr = make_shared<A>();
	shared_ptr<B>b_ptr = make_shared<B>();
	a_ptr->set_quote_ptr(b_ptr);
	b_ptr->set_quote_ptr(a_ptr);
	cout << "a count" << a_ptr.use_count() << endl;
	cout << "b count" << b_ptr.use_count() << endl;
}

int main()
{
	test_ptr();
	system("pause");
	return 0;
}

此时他们之前存在相互引用,但是已经方法已经返回了还没稀释
运行结果:
C++ 四种智能指针详解_第7张图片
解释原因可以参考:https://blog.csdn.net/albertsh/article/details/82286999#commentBox:解释了无法析构的原因,这里简单介绍一下。主要是相互引用时,导致引用记数无法为0;所以无法调用函数进行析构。

  • 接下来解决的方案,是使用weak_ptr来替代其中一个shared_ptr,因为这样赋值的话,不会引发计数变化,修改完的代码如下:
#include "stdafx.h"
#include 
#include 
using namespace std;

class B;
class A{
public:
	A(){
		cout << "A init!" << endl;
	}
	~A(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<B>mm)
	{
		m_b = mm;
	}
private:
	weak_ptr<B> m_b;
};

class B{
public:
	B(){
		cout << "A init!" << endl;
	}
	~B(){
		cout << "A Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<A>mm)
	{
		m_a = mm;
	}
private:
	weak_ptr<A> m_a;
};

void test_ptr()
{
	shared_ptr<A>a_ptr = make_shared<A>();
	shared_ptr<B>b_ptr = make_shared<B>();
	a_ptr->set_quote_ptr(b_ptr);
	b_ptr->set_quote_ptr(a_ptr);
	cout << "a count" << a_ptr.use_count() << endl;
	cout << "b count" << b_ptr.use_count() << endl;
}

int main()
{
	test_ptr();
	system("pause");
	return 0;
}

运行结果:
C++ 四种智能指针详解_第8张图片
可以明显的看出,已经被析构了。

  • weak_ptr还有两个主要的方法lock()expired(),这边简单介绍一下
  • expired主要判断赋值给他的shared_ptr对象是否还在,在的话返回1,不在的话返回0
  • lock()返回赋值它的shared_ptr对象。
    实例代码:
// Weak_Ptr_Use.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include 
#include 
using namespace std;

class B;
class A{
public:
	A(){
		cout << "A init!" << endl;
	}
	~A(){
		cout << "A Destroy" << endl;
		bool is_fired = m_b.expired();
		cout << "endl:" << is_fired << endl;
	}
	void set_quote_ptr(shared_ptr<B>mm)
	{
		m_b = mm;
	}
	weak_ptr<B> m_b;
};

class B{
public:
	B(){
		cout << "B init!" << endl;
	}
	~B(){
		cout << "B Destroy" << endl;
	}
	void set_quote_ptr(shared_ptr<A>mm)
	{
		m_a = mm;
	}
	void show()
	{
		cout << "调用B方法测试" << endl;
	}
private:
	weak_ptr<A> m_a;
};

void test_ptr()
{
	shared_ptr<A>a_ptr = make_shared<A>();
	shared_ptr<B>b_ptr = make_shared<B>();
	a_ptr->set_quote_ptr(b_ptr);
	b_ptr->set_quote_ptr(a_ptr);
	weak_ptr<B> b_weak_ptr = b_ptr;
	if (b_weak_ptr.expired())
	{
			b_weak_ptr.lock()->show();
	}
	cout << "a count" << a_ptr.use_count() << endl;
	cout << "b count" << b_ptr.use_count() << endl;
}

int main()
{
	test_ptr();
	system("pause");
	return 0;
}

运行结果:
C++ 四种智能指针详解_第9张图片


auto_ptr


  • 简单来说,auto_ptr和unique_ptr比较像,但是auto_ptr具备拷贝语义,unique_ptr只有转移所有权的方式
    代码示例:
// Weak_Ptr_Use.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include 
#include 
using namespace std;

class A
{
public:
	A(){
		cout << "A Init" << endl;
	}
	~A()
	{
		cout << "A Destory" << endl;
	}
	void show()
	{
		cout << "demo" << endl;
	}
};

void test_auto_demo()
{
	unique_ptr<A> mma(new A());
	cout << mma << endl;
	auto_ptr<A> A_PTR(new A());
	auto_ptr<A>mm = A_PTR; //实质和std::move()语义一样。
	cout << mm.get() << endl;
	cout << A_PTR.get() << endl;
	A_PTR->show();
}

int main()
{
	test_auto_demo();
	system("pause");
	return 0;
}

运行结果:
C++ 四种智能指针详解_第10张图片
造成在这个的原因是auto_ptrmm = A_PTR; //实质和std::move()语义一样。
这边A_PTR已经把所有权转给mm,此时A_PTR为空指针,空指针必然报错。。
所以unique_ptrauto_ptr更加安全,实质上个人觉得应该是编译器对unique_ptr的处理更加灵活。


智能指针的应用场景


你可能感兴趣的:(C++)