引言
在 C++ 動態(tài)內(nèi)存管理中,除了 auto_ptr
和 unique_ptr
之外,還有一種智能指針 shared_ptr
,它可以讓多個指針共享同一個動態(tài)資源,并且能夠自動釋放資源。shared_ptr
通過引用計數(shù)的方式來管理內(nèi)存,能夠避免程序中出現(xiàn)懸空指針和內(nèi)存泄漏等問題。本文將介紹 shared_ptr
的簡介和使用方法,并提供一個 C++ 模擬實現(xiàn),以幫助讀者更好地理解其原理和實現(xiàn)。
一、簡介
std::shared_ptr
是 C++11 標準庫中的一個智能指針,它可以讓多個指針共享同一個動態(tài)資源,并且能夠自動釋放資源。shared_ptr
通過引用計數(shù)的方式來管理內(nèi)存,能夠避免程序中出現(xiàn)懸空指針和內(nèi)存泄漏等問題。
與 std::auto_ptr
和 std::unique_ptr
不同,std::shared_ptr
可以被多個指針所共享。當一個 shared_ptr
被賦值給另一個 shared_ptr
或者被拷貝構(gòu)造時,它所管理的資源的引用計數(shù)會增加。只有在最后一個shared_ptr
被銷毀時,才會釋放所管理的資源。這種語義被稱為“共享所有權(quán)”。
??std::shared_ptr官方文檔
二、成員函數(shù)
?為了方便用戶使用 shared_ptr
智能指針,shared_ptr<T>
模板類還提供有一些實用的成員方法,它們各自的功能如下表所示
成員方法名 | 功能 |
---|---|
operator=() | 重載賦值號,使得同一類型的 shared_ptr 智能指針可以相互賦值。 |
operator * () | 重載 * 號,獲取當前 shared_ptr 智能指針對象指向的數(shù)據(jù)。 |
operator->() | 重載 -> 號,當智能指針指向的數(shù)據(jù)類型為自定義的結(jié)構(gòu)體時,通過 -> 運算符可以獲取其內(nèi)部的指定成員。 |
swap() | 交換 2 個相同類型 shared_ptr 智能指針的內(nèi)容。 |
reset() | 當函數(shù)沒有實參時,該函數(shù)會使當前 shared_ptr 所指堆內(nèi)存的引用計數(shù)減 1,同時將當前對象重置為一個空指針;當為函數(shù)傳遞一個新申請的堆內(nèi)存時,則調(diào)用該函數(shù)的 shared_ptr 對象會獲得該存儲空間的所有權(quán),并且引用計數(shù)的初始值為 1。 |
get() | 獲得 shared_ptr 對象內(nèi)部包含的普通指針。 |
use_count() | 返回同當前 shared_ptr 對象(包括它)指向相同的所有 shared_ptr 對象的數(shù)量。 |
unique() | 判斷當前 shared_ptr 對象指向的堆內(nèi)存,是否不再有其它 shared_ptr 對象再指向它。 |
operator bool() | 判斷當前 shared_ptr 對象是否為空智能指針,如果是空指針,返回 false ;反之,返回 true 。 |
?當然除此之外,C++11 標準還支持同一類型的 shared_ptr
對象,或者 shared_ptr
和 nullptr
之間,進行 ==
,!=
,<
,<=
,>
,>=
運算。
三、使用示例
下面是一個使用 std::shared_ptr
的示例:
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sp1(new int(42)); // 創(chuàng)建一個指向整數(shù) 42 的 shared_ptr
std::shared_ptr<int> sp2 = sp1; // sp2 和 sp1 現(xiàn)在都指向同一個對象
std::cout << *sp1 << " " << *sp2 << std::endl; // 輸出結(jié)果為 42 42
*sp1 = 10;
std::cout << *sp1 << " " << *sp2 << std::endl; // 輸出結(jié)果為 10 10
sp1.reset(); // 釋放 sp1 的所有權(quán)
std::cout << *sp2 << std::endl; // 輸出結(jié)果為 10
sp2.reset(); // 釋放 sp2 的所有權(quán)
return 0;
}
在這個示例中,我們首先創(chuàng)建了一個指向整數(shù) 42 的 shared_ptr
,然后將其賦值給另一個 shared_ptr
。由于共享所有權(quán)的語義,它們都指向同一個對象。接著,我們修改了 sp1
指向的對象的值,然后釋放了 sp1
的所有權(quán),此時 sp2
仍然可以訪問該對象。最后,我們釋放了 sp2
的所有權(quán),整個示例結(jié)束。
四、C++模擬實現(xiàn)
#include <iostream>
#include <mutex>
using namespace std;
template<class T>
class shared_ptr
{
public:
// 構(gòu)造函數(shù)
explicit shared_ptr(T* ptr = nullptr)
: _ptr(ptr)
, _pcount(new int(1))
, _pmtx(new mutex)
{}
// 析構(gòu)函數(shù)
~shared_ptr()
{
Release();
}
/* 釋放資源
Release() 方法減少引用計數(shù),并根據(jù)引用計數(shù)的值來判斷是否需要刪除指向的堆內(nèi)存對象和引用計數(shù)對象。
在操作之前,我們使用互斥量 _pmtx 進行加鎖,以保證線程安全。*/
void Release()
{
_pmtx->lock();
bool deleteFlag = false;
if (--(*_pcount) == 0)
{
if (_ptr)
{
// 刪除器進行刪除
_del(_ptr);
}
delete _pcount;
deleteFlag = true;
}
_pmtx->unlock();
if (deleteFlag)
{
delete _pmtx;
}
}
// 增加引用計數(shù)
void AddCount()
{
_pmtx->lock();
++(*_pcount);
_pmtx->unlock();
}
// 拷貝構(gòu)造函數(shù)
shared_ptr(const shared_ptr<T>& sp)
: _ptr(sp._ptr)
, _pcount(sp._pcount)
, _pmtx(sp._pmtx)
{
AddCount();
}
// 賦值運算符重載
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_pcount = sp._pcount;
_pmtx = sp._pmtx;
AddCount();
}
return *this;
}
// operator*() 重載
T& operator*()
{
return *_ptr;
}
// operator->() 重載
T* operator->()
{
return _ptr;
}
// get() 方法
T* get()
{
return _ptr;
}
// use_count() 方法
int use_count()
{
return *_pcount;
}
// swap() 方法,交換 2 個 shared_ptr 智能指針的內(nèi)容
void swap(shared_ptr<T>& sp) noexcept
{
std::swap(_ptr, sp._ptr);
std::swap(_pcount, sp._pcount);
std::swap(_pmtx, sp._pmtx);
}
// reset() 方法,重置 shared_ptr 智能指針對象
void reset(T* ptr = nullptr)
{
// 釋放原有資源
Release();
// 重新賦值
_ptr = ptr;
_pcount = new int(1);
_pmtx = new mutex;
}
private:
T* _ptr; // 指向堆內(nèi)存對象的指針
int* _pcount; // 引用計數(shù)的指針
mutex* _pmtx; // 保護引用計數(shù)的互斥量
// 包裝器
function<void(T*)> _del = [](T* ptr)
{
cout << "lambda delete:" << ptr << endl;
delete ptr;
};
};
以上代碼是一個簡化版的 shared_ptr
智能指針模板類的實現(xiàn)。智能指針是 C++ 中的一個重要工具,可以幫助開發(fā)者更方便地管理動態(tài)內(nèi)存。在手動管理內(nèi)存時,很容易出現(xiàn)內(nèi)存泄漏和懸垂指針等問題,而使用智能指針則可以自動管理對象的生命周期,避免這些問題。
該 shared_ptr
類實現(xiàn)了一個引用計數(shù)機制,它通過維護一個引用計數(shù),來判斷指向的堆內(nèi)存對象是否應(yīng)該被釋放。當有多個 shared_ptr
指向同一個堆內(nèi)存對象時,引用計數(shù)會增加;當 shared_ptr
對象銷毀時,引用計數(shù)會減少。當引用計數(shù)為 0 時,就可以釋放堆內(nèi)存對象了。
????注意:該 shared_ptr
類是一個簡化版實現(xiàn),可能存在一些問題,不適用于生產(chǎn)環(huán)境中。在實際開發(fā)中,我們可以使用標準庫中的 shared_ptr
類或其他第三方庫中的智能指針實現(xiàn)。
五、std::shared_ptr的線程安全問題
標準庫中的 std::shared_ptr
是線程安全的,可以在多線程環(huán)境下使用。它通過使用原子操作和引用計數(shù)來實現(xiàn)線程安全。
在 std::shared_ptr
內(nèi)部,引用計數(shù)是一個原子操作,確保多個線程可以安全地對其進行增加和減少操作。當有一個新的 std::shared_ptr
指向同一塊堆內(nèi)存時,引用計數(shù)會增加;當某個 std::shared_ptr
對象銷毀時,引用計數(shù)會減少。只有當引用計數(shù)為 0 時,才會釋放堆內(nèi)存。
此外,std::shared_ptr
還使用了原子操作來保證多個線程之間對智能指針對象的訪問是互斥的。這意味著,在多線程環(huán)境下,不同的線程可以同時拷貝和賦值 std::shared_ptr
對象,而不會出現(xiàn)數(shù)據(jù)競爭的問題。
????注意:雖然 std::shared_ptr
提供了線程安全的引用計數(shù)和訪問控制,但它本身并不保證所指向的對象是線程安全的。如果多個線程同時訪問和修改同一塊內(nèi)存,則需要額外的同步機制來確保線程安全性。
六、總結(jié)
shared_ptr
是C++中的智能指針類,通過引用計數(shù)機制管理堆內(nèi)存對象的生命周期,并使用原子操作確保引用計數(shù)的線程安全性。它支持拷貝構(gòu)造和賦值運算符重載,可以安全地共享指向同一塊堆內(nèi)存的對象。此外,shared_ptr
提供了方便的訪問和操作接口,是一種方便而安全的資源管理工具。
當然,從上述方面來看,shared_ptr
確實是一種非常強大的工具。然而,它也存在一個缺點,即“循環(huán)引用問題”。在下一篇文章中,我將介紹與之相關(guān)的知識點——weak_ptr
。weak_ptr
是一種特殊的智能指針,用于解決 shared_ptr
循環(huán)引用可能導(dǎo)致的內(nèi)存泄漏問題。通過引入weak_ptr
,我們可以打破循環(huán)引用,避免內(nèi)存泄漏,并在需要時安全地訪問對象。敬請期待下一篇文章的發(fā)布!
溫馨提示
感謝您對博主文章的關(guān)注與支持!另外,我計劃在未來的更新中持續(xù)探討與本文相關(guān)的內(nèi)容,會為您帶來更多關(guān)于C++以及編程技術(shù)問題的深入解析、應(yīng)用案例和趣味玩法等。請繼續(xù)關(guān)注博主的更新,不要錯過任何精彩內(nèi)容!文章來源:http://www.zghlxwxcb.cn/news/detail-813307.html
再次感謝您的支持和關(guān)注。期待與您建立更緊密的互動,共同探索C++、算法和編程的奧秘。祝您生活愉快,排便順暢!文章來源地址http://www.zghlxwxcb.cn/news/detail-813307.html
到了這里,關(guān)于【C++入門到精通】智能指針 shared_ptr 簡介及C++模擬實現(xiàn) [ C++入門 ]的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!