1. 程式人生 > 其它 ><一>智慧指標基礎

<一>智慧指標基礎

程式碼1

int main(){  
   //裸指標,手動開闢,需要自己釋放,如果忘記了或者因為
   //程式邏輯導致p沒有釋放,那麼就會導致記憶體洩漏
   int *p=new int(10);

   if(***){
     retur -1;
   }
   delete p;
   return 0;
}

有沒有什麼辦法幫我們管理指標,確保資源釋放?
智慧指標
利用棧上的物件出作用域時自動析構的特徵,來做到資源的自動釋放
問題:是否可以在堆上建立裸指標?語法上沒有問題,但是我們正式希望
棧上物件出作用域能自動析構的特徵來達到自動管理指標的目的,如果
將智慧指標建立在堆上,那又和原來的裸指標使用遇到的問題是一樣的了
需要手動delete

程式碼2

#include <iostream>
using namespace std;


template<typename T>
class MySmartPtr1 {

public:
     MySmartPtr1(T * ptr=nullptr) : _mptr(ptr) {  }
	 ~MySmartPtr1() {
		 delete _mptr;
		 _mptr = nullptr;
	 }

	 T & operator*() { return *_mptr; }//返回的 是 & , 需要修改值
	 T * operator->() { return _mptr; }

private:
	T * _mptr;

};

int main() {
	MySmartPtr1<int> ptr(new int(10));
	*ptr= 200;
	return 0;
}

程式碼2的問題

int main() {
	MySmartPtr1<int> ptr(new int(10));
       
       //使用ptr 拷貝構造ptr2,預設的拷貝構造方式是值拷貝,所以底層
       //_mptr指標 指向的是同一塊記憶體,那麼ptr2 和ptr析構的時候就會有問題了,兩次析構同一片記憶體       
        MySmartPtr1<int> ptr2(ptr); 
	*mptr = 200;
	return 0;
}

如何解決呢?
1:不帶引用計數的智慧指標
auto_ptr C++庫提供
C++11 新標準
scoped_ptr
unique_ptr

程式碼 關於 auto_ptr

int main() {
	auto_ptr<int> ptr1(new int(100));
	auto_ptr<int> ptr2(ptr1);
	*ptr2 = 200;     
        cout<<*ptr1<<endl;//執行報錯,原因見下圖
	return 0;
}

現在不推薦使用auto_ptr
容器中推薦用auto_ptr嗎? vector<auto_ptr> v1; v2(v1); 容器的拷貝構造和容器的賦值容易引起容器元素的拷貝構造和賦值,而auto_ptr的拷貝構造會將原來管理的底層資源(指標)置空

程式碼關於 scoped_ptr

int main() {	
        scope_ptr的處理方式

        scope_ptr<int>(const scope_ptr<int> & src)=delete;//通過直接和諧掉這兩個方法
        scope_ptr<int> & operator=(const scope_ptr<int> & src))=delete;//通過直接和諧掉這兩個方法
	
        return 0;
}

所以scoped_ptr使用的也很少

程式碼關於 unique_ptr

int main() {	
        unique_ptr的處理方式

        unique_ptr<int>(const unique_ptr<int> & src)=delete;//通過直接和諧掉這兩個方法
        unique_ptr<int> & operator=(const unique_ptr<int> & src))=delete;//通過直接和諧掉這兩個方法
	

        unique_ptr<int> ptr1(new int(100));
	unique_ptr<int> ptr2(ptr1);//編譯報錯,嘗試使用已經刪除的函式, 要改成如下!!!


        unique_ptr<int> ptr1(new int(100));	
        unique_ptr<int> ptr2(std::move(ptr1));//編譯OK,為什麼可以呢?因為unique_ptr提供了右值引用的拷貝構造和右值引用的賦值函式,如下

        unique_ptr<int>(const unique_ptr<int> && src){};
        unique_ptr<int> & operator=(const unique_ptr<int> && src)){};


        return 0;
}

//推薦使用

2:帶引用計數的智慧指標(share_ptr,weak_ptr)

帶引用計數的好處:多個智慧指標可以管理同一個資源
帶引用計數:給每一個物件資源,匹配一個引用計數,
智慧指標引用一個資源的時候,給這個資源引用計數加1
當這個智慧指標出作用域不再使用資源的時候,給這個資源引用計數-1,當引用計數不為0的時候,還不能析構這個資源,
當引用計數為0的時候,說明已經沒有外部資源使用這個資源了,那麼就可以析構這個資源了

程式碼3 簡單實現share_ptr

#include <iostream>
using namespace std;


template<typename T>
class RefCount {

public:
	RefCount(T * pSrc = nullptr, int refCount = 0):_pSrc(pSrc),_refCount(refCount) {
	
	}
	void addCount() { this->_refCount++; }
	void deleltCount() { --this->_refCount; }
	int  refCount() { return this->_refCount; }

private:
	T * _pSrc;
	int _refCount = 0;
};



template<typename T>
class MySmartPtr2 {

public:

	//新建立的智慧指標,預設計數器為1
	MySmartPtr2<T> (T * mptr=nullptr): _mptr(mptr){
		_pRef = new RefCount<T>(_mptr,1);
	}

	//拷貝構造
	MySmartPtr2<T>(const MySmartPtr2<T> & _rval) {
		//兩個智慧指標指向相同的資源
		
		this->_mptr = _rval._mptr;
		this->_pRef = _rval._pRef;
		this->_pRef->addCount();	
	}
	//賦值過載
	MySmartPtr2<T> & operator=(const MySmartPtr2<T> & _rval) {
		if (this == &_rval) { retur *this; }
		else {
			this->_pRef->deleltCount();
			int currentCount = this->_pRef->refCount();
			if (currentCount == 0) {
				delete this->_mptr;//銷燬指向的資源
				this->_mptr = nullptr;
				delete _pRef;
				_rPef = nullptr;
			}
			this->_pRef = _rval._pRef;
			this->_mptr = _rval._mptr;
			this->_pRef->addCount();
			return *this;
		}
	}
	~MySmartPtr2<T>() {
	
		this->_pRef->deleltCount();
		int currentCount = this->_pRef->refCount();
		if (currentCount == 0) {
			delete this->_mptr;//銷燬指向的資源
			this->_mptr = nullptr;
			delete _pRef;
			_pRef = nullptr;
		}
	}

	int getRefCount() { return this->_pRef->refCount(); }

private:
	T * _mptr;
	RefCount<T> * _pRef;
};


int main() {
	MySmartPtr2<int> ms1(new int(100)) ;
	
	{
		MySmartPtr2<int> ms2(ms1);
		cout << "RefCount=" << ms1.getRefCount() << endl;

		MySmartPtr2<int> ms3(ms1);
		cout << "RefCount=" << ms1.getRefCount() << endl;

	}
	cout << "RefCount=" << ms1.getRefCount() << endl;


	system("pause");
	
	return 0;
}

share_ptr: 強智慧指標,可以改變資源的引用計數
weak_ptr: 弱智慧指標,不會改變資源的引用計數

強智慧指標:迴圈引用(交叉引用)是什麼問題?什麼結果?怎麼解決?

交叉引用程式碼

class A{
pubic:
    A(){cout<<"A()"<<endl;}
    ~A(){cou<<"~A()"<<endl;}
    share_ptr<B> _ptrb;
}

class B{
pubic:
    B(){cout<<"B()"<<endl;}
    ~B(){cou<<"~B()"<<endl;}
    share_ptr<A> _ptrb;
}

int main(){
     
     share_ptr<A> pa(new A());
     share_ptr<B> pb(new B());
     
     pa->_ptrb=pb;
     pb->_ptra=pa;
    
     cout<<pa.use_count()<<endl;// 2
     cout<<pb.use_count()<<endl;// 2

}

上面程式碼造成new出來的資源無法釋放!!資源洩漏問題

解決:
定義物件的時候,用強智慧指標,引用物件的地方用弱智慧指標

class A{
pubic:
    A(){cout<<"A()"<<endl;}
    ~A(){cou<<"~A()"<<endl;}
    void testA(){
        cout<<"A testA() Function"<<endl;
    }
    weak_ptr<B> _ptrb;
}

class B{
pubic:
    B(){cout<<"B()"<<endl;}
    ~B(){cou<<"~B()"<<endl;}

    void function(){
         share_ptr<A> _tp=_ptrb.lock();//提升方法
         if(_tp!=nullptr){
             _tp->testA();
         }         
    }
    weak_ptr<A> _ptrb; //weak_ptr 弱智慧指標,不會改變引用計數
}

int main(){
     
     share_ptr<A> pa(new A());
     share_ptr<B> pb(new B());
     
     pa->_ptrb=pb;
     pb->_ptra=pa;
    
     pb.function();
     cout<<pa.use_count()<<endl;// 2
     cout<<pb.use_count()<<endl;// 2

}

share_ptr 和 weak_ptr 是執行緒安全的.