栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++智能指针

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++智能指针

智能指针 1. 前言与智能指针的引出

​ 为了方便程序员因为在申请了一块堆内存,之后忘记释放该内存导致的内存泄露的问题,引入智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象,这其中的理念便是C++之父提出的RAII(Resources Acquisition Is Initialization),资源获取即初始化,对象在初始化时获取资源,销毁时释放资源。智能指针在C++11版本之后提供,包含在头文件中,shared_ptr、weak_ptr、unique_ptr、auto_ptr。接下来对这四个智能指针进行逐一介绍。

2. shared_ptr

​ shared_ptr共享指针,可以与其他智能指针通过引用计数的方式共同管理对象指针,每使用它一次,引用次数加1,每析构一次引用次数减一,当引用计数归0后释放对象。

常用API:

API描述
get()获取原始的指针
reset()重置指针,形参为空时相当于相当于析构该智能指针,否则则指向一块新的堆区
use_count()引用计数
unique()是否唯一引用指针
swap(shared_ptr& _Otherptr)该共享指针与传入的共享指针互换(包括引用计数器和原始指针)

构造以及初始化:

//Construct
shared_ptrp1;

shared_ptrp2(nullptr);
shared_ptrp3(new int);

shared_ptrp4(p3); //copy
shared_ptrp5(std::move(p2)); //move

shared_ptrp6(std::unique_ptr(new int));	//使用unique_ptr初始化

cout << "p1.ues_count()" << p1.use_count() << endl;		//0
cout << "p2.ues_count()" << p2.use_count() << endl;		//0
cout << "p3.ues_count()" << p3.use_count() << endl;		//2
cout << "p4.ues_count()" << p4.use_count() << endl;		//2
cout << "p5.ues_count()" << p5.use_count() << endl;		//0
cout << "p6.ues_count()" << p6.use_count() << endl;		//1

//operator=
shared_ptrfoo;
shared_ptrbar;

foo = bar; //copy

// 使用make_shared进行初始化
bar = std::make_shared(20); 
std::unique_ptrunqiue(new int(23)); 

shared_ptrfoo = make_shared(10);
auto baz = make_shared>(20, 20);
cout << "foo:" << *foo.get() << endl;
cout << baz->first << baz->second << endl;


foo = std::move(unqiue); //move  unique_ptr

​ 建议:使用make_shared进行初始化。注意不要一个原始指针初始化多个shared_ptr,否则会引起二次释放同一内存的问题。同时注意循环引用的问题:即内存无法真确释放,导致内存泄漏。解决循环引用的方法会在后面提及。

//reset
shared_ptrsp; //empty
sp.reset(new int(20));
cout << "sp: " << *sp << endl;
sp.reset(new int);
*sp = 30;
cout << "sp: " << *sp << endl;

//delete
sp.reset();

// get()
int* p = new int(20);
shared_ptra(p);

​ 同时shared_ptr重载了*、->、=操作符号,赋予了智能指针指针的取址和赋值行为。

cout << *a.get() << endl;	//20
cout << *a << endl;			//20
cout << *p << endl;			//20

//operator ->
shared_ptrop1;
shared_ptrop2(new object);
op1 = op2;

cout << "op1->a:" << op1->a << endl;
 
// unique()
{
shared_ptrp1;
cout << "p1.unique(): " << p1.unique() << endl;	//false(empty)
shared_ptrp2(new int);
p1 = p2;
cout << "p1.unique(): " << p1.unique() << endl; //false(2)
cout << "p2.unique(): " << p1.unique() << endl;	//false(2)

shared_ptrp3(new int);
cout << "p3.unique(): " << p3.unique() << endl; //true
}

//swap
{
shared_ptrp1(new int(20));
shared_ptrp2(new int(30));
shared_ptrp3(p2);
p1.swap(p2);

cout << "after swap:" << endl;
cout << "*p1.get()" << *p1.get() << endl;	//30
cout << "*p2.get()" << *p2.get() << endl;	//20
cout << "p1.use_count()" << p1.use_count() << endl; //2
cout << "p2.use_count()" << p2.use_count() << endl; //1
}
3. weak_ptr

​ weak_ptr是强指针弱化的引用。通常weak_ptr与shared_ptr进行连用,可以使用shared_ptr进行初始化,但不参与shared_ptr的引用计数,它有自己的计数器,并且不参与对象的生命周期。没有重载operator*和->它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。

常用API:

API描述
lock()获取管理的共享指针
expired()当前管理的指针是否无效,无效返回True,有效false。等价于use_count()==0
reset()重置该指针
swap(weak_ptr&_weak)互换weak_ptr(包括计数器和原始指针)

构造:

// construct
{
    shared_ptrsp(new int);
    //default
    weak_ptrwp1;
    //copy

    weak_ptrwp2(wp1);
    // from shared_ptr
    weak_ptrwp3(sp);

    cout << "use_count:" << endl;
    cout << "wp1:" << wp1.use_count() << endl; //0
    cout << "wp1:" << wp2.use_count() << endl; //0
    cout << "wp1:" << wp3.use_count() << endl; //1
    cout << "sp:" << sp.use_count() << endl;  //1
}

lock和expired

{
    shared_ptrsp1,sp2;
    weak_ptrwp;

    sp1 = make_shared(10);
    wp = sp1;

    sp2 = wp.lock(); //return shared_ptr
    sp1.reset();

    sp1 = wp.lock();
    //cout << *sp1 << endl;
    //cout << *sp2 << endl;
    cout << "wp_use_count:"  <<  wp.use_count() << endl; //2
    cout << "sp1_use_count:" << sp1.use_count() << endl;  //2
    cout << "sp2_use_count:" << sp2.use_count() << endl;  //2
}

// expired
{
    object obj;
    shared_ptrsp = make_shared();
    cout << "sp.get()->a:" << sp.get()->a << endl;

    weak_ptrwp(sp);
    cout << "wp.expired():" << wp.expired() << endl; //0
    sp.reset();
    cout << "wp.expired():" << wp.expired() << endl; //1
}
 

swap

//swap
{
    shared_ptrsp1 = make_shared(20);
    shared_ptrsp2 = make_shared(30);
    shared_ptrsp3(sp2);

    weak_ptrwp1 = sp1;
    weak_ptrwp2 = sp2;

    cout << "wp1 use_count:" << wp1.use_count() << endl; //1
    cout << "wp2 use_count:" << wp2.use_count() << endl; //2
    cout << "after swap:n";
    wp1.swap(wp2);
    cout << "wp1 use_count:" << wp1.use_count() << endl; //2
    cout << "wp2 use_count:" << wp2.use_count() << endl; //1
    cout << "*wp2.lock(): " << *wp2.lock() << endl; //20
}
4. auto_ptr

​ auto_ptr是C++98中提供的一个类,构造时获取对象管理权,析构时释放,在C++11中被废除,原因便是auto_ptr的不安全性。下面看一段代码来了解它为什么不安全。

int* ptr = new int(20);
auto_ptrp1(ptr);
auto_ptrp2(ptr);

​ 运行上面代码,程序会崩溃,原因便是同一个指针指向的内存空间不能由两个auto_ptr进行管理,否则会析构两次,类似于浅拷贝的道理。

​ 再来看下一段代码:

int* ptr = new int(20);
auto_ptrp1(ptr);
auto_ptrp2;
p2 = p1;
cout << *p2 << endl; //20
cout << *p1 << endl; //此时的p1已经转移的资源控制权 是一个野指针

​ 上述代码中,如果auto_ptr使用赋值操作符,实际上是使得p1的资源控制权转移到了p2上(相当于使用了std::move)。之后p1便是一个野指针了,使用野指针是非常危险的。

​ 综上所述,auto_ptr的使用非常的不安全,由此延伸出替代auto_ptr的指针—unique_ptr。

5. unique_ptr

​ unique_ptr在C++11中引入,用于替代不安全的auto_ptr,它持有对对象的都有权(两个unique_ptr不能指向同一对象),同时也无法复制到其他unique_ptr。只能通过std::move进行资源管理权的转移。

常用API:

API描述
get()获取原始指针
release()释放Unique_ptr,返回原始指针

构造:

// construct
{
    unique_ptr up1;
    up1.reset(new int(20));
    unique_ptr up2(new int(10));
    // 没有copy语句
	// up2 = up1; error
    
    unique_ptr up3(std::move(up2));	//控制权转移
    cout << *up3.get() << endl;				//10

    unique_ptrup4(new int[3]{ 1,2,34 });
    cout << "up4[2]:" << up4[2] << endl;  //34
}

release

{
    unique_ptr up1(new int(30));
    int* ptr = up1.release();
    cout << "* ptr:"<< * ptr << endl;
    delete ptr;
}
6. enable_shared_from_this

​ 在了解enable_shared_from_this这个类之前,我们首先先来看一个场景,假如一个类对象被shared_ptr管理,另一个shared_ptr同时也需要这个类的shared_ptr进行初始化,写成代码的话如下:

class TestObejct {
public:
	shared_ptr getSharedPtr() {
		shared_ptr tempPtr(this);
		return tempPtr;
	}
	~TestObejct() {
		cout << "TestObejct Destructn";
	}
};
void enablesharedTest() {
	shared_ptrp1(new TestObejct);
	shared_ptrp2(p1->getSharedPtr());
	cout << p1.use_count()<< endl;	//1
	cout << p2.use_count()<< endl;	//1
}

结果如下:

​ 会发现,该类析构了两次,同时shared_ptr的引用计数始终为1。这是因为p1、p2都认为自己是该实例的唯一持有者,所以引用计数没有加1,最后重复释放导致程序崩溃。

​ 为了解决该问题,我们使用enable_shared_from_this,将上述代码改为如下代码:

class TestObejct:public enable_shared_from_this{
public:

	shared_ptr getSharedPtr() {
		return shared_from_this();
	}

	~TestObejct() {
		cout << "TestObejct Destructn";
	}
};

​ 结果正确了,这里我们便可知道std::enable_shared_from_this 是能让一个对象(且已被一个 std::shared_ptr 对象管理)安全地生成其他额外的 std::shared_ptr实例共享对象所有权。我们可以查看std::enable_shared_from_this的源码。

template 
class enable_shared_from_this { // provide member functions that create shared_ptr to this
public:
    using _Esft_type = enable_shared_from_this;

    _NODISCARD shared_ptr<_Ty> shared_from_this() {
        return shared_ptr<_Ty>(_Wptr);
    }

    _NODISCARD shared_ptr shared_from_this() const {
        return shared_ptr(_Wptr);
    }
   ....
private:
    ...
    mutable weak_ptr<_Ty> _Wptr;  
};

​ 可以发现,它内部实现的原理,其实是存着一个weak_ptr的,在调用shared_from_this()函数时,再利用该weak_ptr初始化生成一个新的shared_ptr进行返回。

​ 同时std::enable_shared_from_this还可以将shared_ptr转换为裸指针后,重新转换为shared_ptr:

class TestObejct:public enable_shared_from_this{
public:
	shared_ptr getSharedPtr() {
		return shared_from_this();
	}
	~TestObejct() {
		cout << "TestObejct Destructn";
	}
};
void enablesharedTest() {
	shared_ptrp1(new TestObejct);
	TestObejct* obj = p1.get();
	shared_ptrp2;
	p2 = obj->shared_from_this();
    
	cout << p1.use_count() << endl; //2
	cout << p2.use_count() << endl; //2
}
7. 循环引用

​ 了解循环引用之前,我们先来讨论以下情景,有两个类,成员变量中互相保存着含有对方类型的shared_ptr,同时创建两个类的实例用shared_ptr进行管理。这时两类的内存释放是如何的呢?这里我们可以写下如下代码:

class Child;
class base {
public:
	base(){}
	void SetChild(shared_ptrInChild) {
		_child = InChild;
	}
	~base()
	{
		cout << "base Destructn";
	}
private:
	shared_ptr _child;
};
class Child {
public:
	Child(){}
	void SetParent(shared_ptrInbase) {
		_base = Inbase;
	}
	~Child()
	{
		cout << "Child Destructn";
	}
private:
	shared_ptr _base;
};

int main(){
    weak_ptrwp1;
	weak_ptrwp2;
	{
		shared_ptrbPtr(new base);
		shared_ptrcPtr(new Child);
        wp1 = bPtr;
		wp2 = cPtr;
		bPtr->SetChild(cPtr);
		cPtr->SetParent(bPtr);
		cout << bPtr.use_count() << endl; //2
		cout << cPtr.use_count() << endl; //2
	}
    cout << wp1.use_count() << endl; //1
	cout << wp2.use_count() << endl; //1
	return 0;
}

结果:

​ 可以看到,两类并没有被释放,因为二者的引用计数都还为1。导致的原因是:一开始初始化的时候,将bPtr和cPtr分别复制给二类私有成员中的共享指针,所以两个共享指针的引用计数加1,离开{}作用域后,bPtr和cPtr两个共享指针便开始析构,引用数减1,这里拥有二类实例的资源控制权便只有二类中私有成员下的共享指针(_base和_Child),但这两个指针析构的条件,是对方类析构的时候。所以这时候,双方都在等双方析构,导致无限等待,从而都无法析构导致了内存泄漏,这便是循环引用。

​ 解决的办法很简单,可以将其中一个类的shared_ptr指定为weak_ptr,便可以打破循环。

8. 实现Shared_ptr和Weak_ptr
#pragma once
template
class WeakPtr;

class RefCounter {
public:
	RefCounter() :_UseCount(1), _WeakCount(1) {};

	inline void _InCreaseUse() {
		++_UseCount;
	}


	inline unsigned int _DeCreaseUse() {
		--_UseCount;
		return _UseCount;
	}


	inline void _InCreaseWeak() {
		++_WeakCount;
	}


	inline unsigned int _DeCreaseWeak() {
		--_WeakCount;
		return _WeakCount;
	}

	inline unsigned int _GetUse()const {
		return _UseCount;
	}

	inline unsigned int _GetWeak()const {
		return _WeakCount;
	}

private:
	unsigned int _UseCount{0};  //强指针引用次数
	unsigned int _WeakCount{0}; //弱指针引用次数
};

template
class Ptrbase {
public:
	void Destroy() {
		if (_Ptr) {
			cout << "delete " << *this->_Ptr << endl;
			delete this->_Ptr;
			this->_Ptr = nullptr;
		}
	}

	void Release() {
		if (_RefCounter) {
			delete this->_RefCounter;
			this->_RefCounter = nullptr;
		}
	}

	inline unsigned int Use_Count()const {
		return this->_RefCounter ? this->_RefCounter->_GetUse() : 0;
	}

protected:
	ElementType* _Ptr{nullptr};
	RefCounter* _RefCounter{nullptr};
};





template
class SharedPtr :public Ptrbase {
	friend class WeakPtr;
public:
	SharedPtr(){
		this->_Ptr = nullptr;
		this->_RefCounter = nullptr;
	}

	explicit SharedPtr(ElementType* Elem) {
		this->_Ptr = Elem;
		this->_RefCounter = new RefCounter;
	}

	SharedPtr(SharedPtr& Elem) {
		this->_Ptr = Elem._Ptr;
		Elem._RefCounter->_InCreaseUse();
		this->_RefCounter = Elem._RefCounter;
	}

	SharedPtr(WeakPtr& Elem) {
		if (Elem._Ptr) {
			this->_Ptr = Elem._Ptr;
			Elem._RefCounter->_InCreaseUse();
			this->_RefCounter = Elem._RefCounter;
		}
	}
	
	SharedPtr& operator=(SharedPtr &Elem) {
		if (this->_Ptr != Elem._Ptr) {
			this->Destroy();
			this->_Ptr = Elem._Ptr;
			this->_RefCounter->_InCreaseUse();
			this->_RefCounter = Elem._RefCounter;
		}
	}


	~SharedPtr() {
		this->reset();
	}

	inline void reset() {
		if (this->_RefCounter && this->_RefCounter->_DeCreaseUse() == 0&&this->_Ptr) {
			this->Destroy();
			if (this->_RefCounter->_GetWeak() == 0) {
				this->Release();
			}
		}
	}

	inline void reset(ElementType* Elem) {
		reset();
		this->_Ptr = Elem._Ptr;
		this->_RefCounter = Elem._RefCounter;
	}

	inline void swap(SharedPtr& Elem) {
		if (this->_Ptr != Elem._Ptr&&Elem._Ptr!=nullptr&&this->_Ptr!=nullptr) {
			ElementType* tempPtr = this->_Ptr;
			RefCounter* tempCounter = this->_RefCounter;
			this->_Ptr = Elem._Ptr;
			this->_RefCounter = Elem._RefCounter;
			Elem._Ptr = tempPtr;
			Elem._RefCounter = tempCounter;
		}
	}


	inline ElementType* get()const {
		return this->_Ptr;
	}

	inline bool isUnique()const {
		return this->_RefCounter ? this->_RefCounter->_GetUse() == 1 : 0;
	}

	inline ElementType*  operator->() const{
		return this->_Ptr;
	}

	inline ElementType& operator*()const{
		return *this->_Ptr;
	}
};



template
class WeakPtr :public Ptrbase {
public:
	friend class SharedPtr;
	WeakPtr(){
		this->_Ptr = nullptr;
		this->_RefCounter = nullptr;
	};

	explicit WeakPtr(SharedPtr& Elem) {
		this->_Ptr = Elem._Ptr;
		Elem._RefCounter->_InCreaseWeak();
		this->_RefCounter = Elem._RefCounter;
	}

	~WeakPtr() {
		if (this->_RefCounter&&this->_RefCounter->_DeCreaseWeak() == 0) {
			this->Destroy();
		}
	}

	WeakPtr(const WeakPtr& Elem) {
		if (Elem._Ptr != this->_Ptr) {
			this->Release();
		}
	}


	inline SharedPtr& lock() {
		SharedPtr temp(*this);
		return temp;
	}

	inline bool expired()const{
		return this->_Ptr ? false : true;
	}
};
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号