代碼1 int main(){ //裸指針,手動開闢,需要自己釋放,如果忘記了或者因為 //程式邏輯導致p沒有釋放,那麼就會導致記憶體泄漏 int *p=new int(10); if(***){ retur -1; } delete p; return 0; } 有沒有什麼辦法幫我們管理指針,確保資 ...
代碼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
代碼關於 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
}
出main函數的時候,
第1步 先析構pb,pb在析構的時候,發現除了自己引用B對象,其他地方(A對象內)還有引用B對象的,所以 B資源無法釋放
第2步,再析構pa,pa在析構的時候,發現除了自己引用A對象,其他地方(B對象內)還有引用A對象的,所以 導致A對象也無法釋放
出了函數這兩堆上的資源都沒有被釋放掉,泄漏!
上面代碼造成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
share_ptr是C++11新添加的智能指針,它限定的資源可以被多個指針共用。
只有指向動態分配的對象的指針才能交給 shared_ptr 對象托管。將指向普通局部變數、全局變數的指針交給 shared_ptr 托管,編譯時不會有問題,但程式運行時會出錯,因為不能析構一個並沒有指向動態分配的記憶體空間的指針
weak_ptr
weak_ptr是一種用於解決shared_ptr相互引用時產生死鎖問題的智能指針。如果有兩個shared_ptr相互引用,那麼這兩個shared_ptr指針的引用計數永遠不會下降為0,資源永遠不會釋放。weak_ptr是對對象的一種弱引用,它不會增加對象的use_count,weak_ptr和shared_ptr可以相互轉化,shared_ptr可以直接賦值給weak_ptr,weak_ptr也可以通過調用lock函數來獲得shared_ptr。
weak_ptr指針通常不單獨使用,只能和 shared_ptr 類型指針搭配使用。將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。一旦最後一個指向對象的shared_ptr被銷毀,對象就會被釋放。即使有weak_ptr指向對象,對象也還是會被釋放。
weak_ptr並沒有重載operator->和operator *操作符,因此不可直接通過weak_ptr使用對象,典型的用法是調用其lock函數來獲得shared_ptr示例,進而訪問原始對象。
share_ptr 和 weak_ptr 是線程安全的.