智能指针(一)

不想你离开。 提交于 2019-11-30 01:07:27

1 引入

C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。管理是麻烦点(e.g. 手动释放等),但无伤大雅,勉强可以接受;但要命的是,容易出问题:

  • 内存泄露:忘记手动释放 或 由于异常存在跳过释放等,都会导致内存泄漏;
  • 悬挂指针:
  • 二次释放:多次释放同一指针会导致程序崩溃。

为了解决该问题,C++ 11 引入智能指针概念使内存管理变得更为方便,且不易出错:

  • 从较浅的层面看,智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针;
  • 智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。

智能指针包含在头文件<memory>中,包括:shared_ptr、unique_ptr、weak_ptr。

2 shared_ptr

对于一个对象,允许有多个shared_ptr指向该对象,并使用引用计数方式统计指向该对象的shared_ptr个数。

  • 每多一个shared_ptr指向该对象,引用计数加一;每少一个shared_ptr指向该对象,引用计数减一;当引用计数为0时,自动删除所指向的堆内存;
  • 注意不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存;
  • shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏;
#include <iostream>
#include <memory>
using namespace std;

void testShared() {
	shared_ptr<int> sptrL1(new int);
	shared_ptr<int> sptrL2(sptrL1);		//拷贝构造1方式添加一个指向该对象的shared_ptr
	shared_ptr<int> sptrL3 = sptrL2;	//拷贝构造2方式添加一个指向该对象的shared_ptr
	shared_ptr<int> sptrL4;				//声明一个未指向任何对象的空shared_ptr
	sptrL4 = sptrL3;					//赋值方式添加一个指向该对象的shared_ptr

	//输出引用计数个数,即:有多少个shared_ptr指向该对象
	cout << sptrL1.use_count() << endl;	
	cout << sptrL2.use_count() << endl;
	cout << sptrL3.use_count() << endl;
	cout << sptrL4.use_count() << endl;

	//获取原始指针
	cout << sptrL1.get() << endl;		
	cout << sptrL2.get() << endl;
	cout << sptrL3.get() << endl;
	cout << sptrL4.get() << endl;

	//使用make_shared方式初始化shared_ptr
	shared_ptr<int> sptrR1 = make_shared<int>(10);
	cout << sptrR1.use_count() << endl;

	//使sptrL4指向sptrR1所指的对象
	sptrL4 = sptrR1;
	cout << sptrL1.use_count() << endl;
	cout << sptrR1.use_count() << endl;
}

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

//输出结果
4
4
4
4
012AC768
012AC768
012AC768
012AC768
1
3
2

3 unique_ptr

对于一个对象,unique_ptr “唯一” 拥有该对象(i.g. 同一时刻只能有一个unique_ptr指向该对象),禁止通过拷贝构造,赋值等方式来使其它unique_ptr也指向该对象。

  • unique_ptr 的生命周期:从创建开始,到其离开作用域;
  • 当 unique_ptr 离开其作用域时,若其指向某个对象,则将该对象销毁(默认使用delete操作符,用户可指定其它操作);
  • 在 unique_ptr 生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权,通过move方法转移所有权等。
#include <iostream>
#include <memory>
using namespace std;

void testUnique() {
	unique_ptr<int> uptr1(new int);

	//unique_ptr<int> uptr2(uptr1);		//不允许以拷贝构造1方式使其它unique_ptr指向该对象
	//unique_ptr<int> uptr2 = uptr1;	//不允许以拷贝构造2方式使其它unique_ptr指向该对象
	//unique_ptr<int> uptr2;
	//uptr2 = uptr1;					//不允许以赋值方式使其它unique_ptr指向该对象

	cout << uptr1.get() << endl;

	uptr1.reset(new int);				//指向新的对象
	cout << uptr1.get() << endl;

	uptr1.release();					//释放所有权
	cout << uptr1.get() << endl;

	//使用make_unique方式初始化unique_ptr
	unique_ptr<int> uptr2 = make_unique<int>(11);
	cout << uptr2.get() << endl;

	uptr1 = move(uptr2);				//转移所有权
	cout << uptr1.get() << endl;
	cout << uptr2.get() << endl;

	shared_ptr<int> sptr = move(uptr1);	//所有权转移给shared_ptr
	cout << sptr.get() << endl;
	cout << uptr1.get() << endl;
}

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

//输出结果
0124DF58
0124DF88
00000000
0124DF58
0124DF58
00000000
0124DF58
00000000

4 weak_ptr

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它不具有普通指针的行为(i.g. 没有重载 operator* 和 -> ),它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。

  • weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权;但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
  • 成员函数 use_count() 用于观测资源的引用计数;
  • 成员函数 expired() 用于判断资源(被 weak_ptr 观测的资源 或 被 shared_ptr 管理的资源)是否存在(等价于use_count()==0,但更快);
  • 成员函数 lock() 用于从被观测的 shared_ptr 获得一个可用的shared_ptr对象, 从而操作资源;但当expired()==true的时候,lock() 函数将返回一个存储空指针的shared_ptr。
#include <iostream>
#include <memory>
using namespace std;

void testWeak() {
	shared_ptr<int> sptr1 = make_shared<int>(10);
	cout << sptr1.use_count() << endl;	//引用计数个数

	weak_ptr<int> wptr(sptr1);
	cout << wptr.use_count() << endl;	//观测到的引用计数个数

	if (!wptr.expired()) {
		shared_ptr<int> sptr2 = wptr.lock();	//添加一个新的指向观测资源的shared_ptr
		cout << sptr1.use_count() << endl;		//引用计数增加
		cout << sptr2.use_count() << endl;
		cout << wptr.use_count() << endl;

		cout << *sptr1 << endl;
		*sptr2 = 100;							//使用新得到的shared_ptr修改资源值
		cout << *sptr1 << endl;
	}
}

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

//输出结果
1
1
2
2
2
10
100

5 实现原理

#include <iostream>
using namespace std;

template<typename T>
class SmartPointer {
private:
	T* _ptr;
	size_t* _count;					//可以可以
public:
	SmartPointer(T* ptr = nullptr) :
		_ptr(ptr) {
		if (_ptr) {
			_count = new size_t(1);
		}
		else {
			_count = new size_t(0);
		}
	}

	SmartPointer(const SmartPointer& ptr) {
		if (this != &ptr) {
			this->_ptr = ptr._ptr;
			this->_count = ptr._count;
			(*this->_count)++;							//666
		}
	}

	SmartPointer& operator=(const SmartPointer& ptr) {
		if (this->_ptr == ptr._ptr) {
			return *this;
		}

		if (this->_ptr) {
			(*this->_count)--;
			if (this->_count == 0) {
				delete this->_ptr;
				delete this->_count;
			}
		}

		this->_ptr = ptr._ptr;
		this->_count = ptr._count;
		(*this->_count)++;
		return *this;
	}

	T& operator*() {
		assert(this->_ptr == nullptr);
		return *(this->_ptr);

	}

	T* operator->() {
		assert(this->_ptr == nullptr);
		return this->_ptr;
	}

	~SmartPointer() {
		(*this->_count)--;
		if (*this->_count == 0) {
			delete this->_ptr;
			delete this->_count;
		}
	}

	size_t use_count() {
		return *this->_count;
	}
};

void testPrinciple() {
	SmartPointer<int> sp(new int(10));
	SmartPointer<int> sp2(sp);
	SmartPointer<int> sp3(new int(20));
	sp2 = sp3;
	std::cout << sp.use_count() << std::endl;
	std::cout << sp3.use_count() << std::endl;
}

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

参考文献

https://www.cnblogs.com/wxquare/p/4759020.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!