最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ unique_ptr、shared_ptr、weak_ptr的區(qū)別小結

 更新時間:2025年07月10日 09:46:53   作者:Zane Joy  
本文主要介紹了C++ unique_ptr、shared_ptr、weak_ptr的區(qū)別小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

unique_ptr

所有權轉移

unique_ptr不能被拷貝和用于賦值,因為unique_ptr刪掉了這兩個函數

但是底層源碼重載了傳右值的拷貝構造
所以可以通過std::move來通過轉移所有權

unique_ptr<Data> p6(new Data());
//不可復制構造和賦值復制
//unique_ptr<Data>p7 = p6; 錯誤

//p6釋放所有權 轉移到p7
unique_ptr<Data>p7 = move(p6);

unique_ptr<Data>p8(new Data());
p7 = move(p8);//重新移動賦值,原有的p6會被釋放掉
//重置空間,原空間清理
p7.reset(new Data());

所有權釋放

注意!當unique_ptr釋放所有權以后智能指針就不會再管理這塊空間,需要自己手動釋放空間!

//釋放所有權
unique_ptr<Data>p9(new Data());
auto ptr9 = p9.release();//注意,release釋放所有權以后要自己清理空間
delete ptr9;//?。。。。。?

make_unique 和直接(new)unique_ptr

區(qū)別一 :需要一次性處理多個資源分配的地方make_unique比unique_ptr更安全

void process_data(
    std::unique_ptr<Data> p1, 
    std::unique_ptr<Data> p2
);

process_data(
    std::unique_ptr<Data>(new Data("A")),  // 分配資源 A
    std::unique_ptr<Data>(new Data("B"))   // 分配資源 B
);

編譯器在構造函數參數時,?執(zhí)行順序是不確定的。可能的執(zhí)行順序例如:

  1. new Data(“A”) → 成功,得到一個裸指針 A
  2. new Data(“B”) → 成功,得到一個裸指針 B*
  3. 構造unique_ptr 接管 A*
  4. 構造 unique_ptr 接管 B*

如果中間發(fā)生異常:

  1. new Data(“A”) → 成功,得到 A*
  2. new Data(“B”) → ?拋出異常(例如內存不足)? ?此時 A* 尚未被 unique_ptr 接管!? 異常被拋出后,裸指針 A* 無法被自動釋放 → ?內存泄漏。

如果選用make_unique

process_data(
    std::make_unique<Data>("A"),  // 直接構造并接管資源 A
    std::make_unique<Data>("B")   // 直接構造并接管資源 B
);

此時每一步的執(zhí)行:

  1. make_unique(“A”) → ?立即構造對象并封裝到 unique_ptr,無裸指針暴露
  2. make_unique(“B”) → 同上 如果 make_unique(“B”) 拋出異常:
    ?make_unique(“A”) 已經返回的 unique_ptr 會正常析構 → 資源 A 被自動釋放沒有泄漏!

區(qū)別二 :直接new支持自定義刪除器

new支持在構造 unique_ptr 時指定自定義刪除器

std::unique_ptr<MyClass, Deleter> p(new MyClass, custom_deleter);

但是make_unique不支持,只能使用默認的 delete 操作符。

shared_ptr

shared_ptr智能指針指向同一個對象的不同成員

sc2和sc3 分別 指向sc1的index1成員和index2成員,使sc1的引用計數+2

class Data
{
public:
	Data() {

		cout<< "Begin Data" << endl;
	}
	~Data() { cout<< "End Data" << endl; }
	int index1 = 0;
	int index2 = 0;
};

{
	shared_ptr<Data>sc1(new Data);
	//打印引用計數 = 1
	cout << "sc1.use_count() = " << sc1.use_count() << endl;

	shared_ptr<int>sc2(sc1,&sc1->index1);//引用計數+1
	shared_ptr<int>sc3(sc1, &sc1->index2);//引用計數+1
	//打印引用計數 = 3
	cout << "sc1.use_count() = " << sc1.use_count() << endl;
}

循環(huán)引用問題

當兩個或多個對象通過 shared_ptr ?互相持有對方時,它們的引用計數永遠不會歸零,導致內存無法釋放。

	class A
	{
	public:
		A() { cout << "Create A" << endl; }
		~A() { cout << " Drop A " << endl; }

		void Do()
		{
			cout << "Do b2.use_count() = " << b2.use_count() << endl;
			auto b = b2.lock(); //復制一個shared_ptr 引用計數加一
			cout << "Do b2.use_count() = " << b2.use_count() << endl;

		}

		shared_ptr<B> b1;//強智能指針
		weak_ptr<B> b2;  //弱智能指針
	};
	class B
	{
	public:
		B() { cout << "Create B" << endl; }
		~B(){ cout << " Drop B " << endl; }
		shared_ptr<A> a1;//強智能指針
		weak_ptr<A> a2;  //弱智能指針

	};

	{
		auto a = make_shared<A>();//a引用計數+1
		auto b = make_shared<B>();//b引用計數+1

		a->b1 = b;//b引用計數+1
		//出作用域前,引用計數為2
		cout << "a->b1 = b;b.use_count()=" << b.use_count() << endl;

		b->a1 = a;//a引用計數+1
		//出作用域前,引用計數為2
		cout << "b->a1 = a;a.use_count()=" << a.use_count() << endl;

	}

由于調用問題,導致出作用域以后a和b的引用計數都還是1,所以空間沒有被釋放

改成使用weak_ptr

	{
		auto a = make_shared<A>();
		auto b = make_shared<B>();

		a->b2 = b;//weak_ptr 引用計數不加一
		a->Do();//Do函數里引用計數加一,出Do函數作用域減一
		cout << "a->b2 = b;b.use_count()=" << b.use_count() << endl;

		b->a2 = a;//引用計數不加一
		cout << "b->a2 = a;a.use_count()=" << a.use_count() << endl;

	}//不會產生循環(huán)引用問題

為什么weak_ptr能解決shared_ptr的循環(huán)引用問題

weak_ptr 本身不擁有資源所有權
它只是觀察 shared_ptr 管理的對象,不會增加引用計數。

所以a->b2 = b;這句不會增加b的引用計數
同理b->a2 = a;這句也不會增加a的引用計數

但是由于weak_ptr只是一個觀察者,無法訪問任何資源,僅“觀察”資源,不擁有所有權如果想要訪問資源該怎么辦呢?

如同a->Do();里做的那樣
只要原 shared_ptr(即 a)未釋放資源,就可以通過 lock() 獲取有效的 shared_ptr 并訪問。

void Do()
{
	cout << "Do b2.use_count() = " << b2.use_count() << endl;
	auto b = b2.lock(); //返回一個shared_ptr 引用計數加一
	
	//這里還可以做其他的訪問shared_ptr資源的操作

	cout << "Do b2.use_count() = " << b2.use_count() << endl;

}//出作用域加上的那個引用計數自動-1

通過weak_ptr.lock()返回 一個 shared_ptr 并將引用計數加一

(注意!只有當原shared_ptr對象還存在的時候才會返回shared_ptr,否者返回nullptr)

除了放函數里,還能放判斷條件里

    if (auto a_shared = b->a_weak.lock()) {
        // 返回nullptr 說明shared_ptr被釋放
        //不會執(zhí)行此處
    } else {
 		//引用計數+1
        std::cout << "A is already destroyed!" << std::endl;  // 輸出此句
    }//引用計數-1

通過這種方式,weak_ptr 可以安全地觀察資源,而不會導致循環(huán)引用或內存泄漏

make_shared 和 直接 new 一個shared_ptr的區(qū)別

區(qū)別一:make_shared資源分配更安全

原因和make_unique一樣,這里就不再贅述了

區(qū)別二:直接new支持自定義刪除器

區(qū)別三:內存分配方式不同

由于shared_ptr除了維護對象本身的內存以外還要維護一個控制塊

  • ?強引用計數?(use_count):記錄有多少個 shared_ptr 共享對象
  • ?弱引用計數?(weak_count):記錄有多少個weak_ptr 觀察對象?
  • 自定義刪除器?(如果存在)。 ?
  • 對象指針?(指向實際分配的對象)。

make_shared 在底層會 ?一次性分配一塊連續(xù)內存,既存儲對象本身,也存儲控制塊。

但是new shared_ptr會有兩次分配

std::shared_ptr<MyClass> p(new MyClass);
  1. 第一次分配:new MyClass 分配對象內存。
  2. ?第二次分配:shared_ptr 構造函數內部為控制塊分配內存。

為什么make_unique和直接new unique_ptr都是一次分配內存

因為unique_ptr 不需要維護引用計數,因此 ?沒有控制塊。無論通過 make_unique 還是直接 new,都只需分配對象內存

shared_ptr合并分配的缺點

對象和控制塊內存綁定,即使所有 shared_ptr 銷毀,若仍有 weak_ptr 存在,對象內存仍然需等待控制塊釋放(但析構函數會被及時調用)

shared_ptr控制塊內存的延遲釋放是內存泄漏嗎?

由于make_shared 是一次性分配一塊連續(xù)內存,同時存儲 ?對象實例 和 ?控制塊?(包含引用計數、弱引用計數等)

所以當沒有weak_ptr存在時

通過make_shared建立的shared_ptr:

  • 對象析構函數立即被調用。
  • 整塊內存(對象 + 控制塊)立即釋放。

通過new建立的shared_ptr:

  • 對象內存立即釋放。
  • 控制塊內存也立即釋放。

當有weak_ptr存在時

通過make_shared建立的shared_ptr:

  • 對象析構函數被調用(資源清理)。 ?
  • 對象內存和控制塊內存暫時保留?(直到所有 weak_ptr 也被銷毀)。

通過new建立的shared_ptr:

  • 對象內存立即釋放(僅保留控制塊內存)。

由于make_shared對象和控制塊內存是連續(xù)的,無法單獨釋放對象內存。所以必須等待所有 weak_ptr 銷毀后,?整塊內存才能一起釋放。

那這是內存泄漏嗎?

不是!? 內存泄漏的定義是:?無法再訪問且未釋放的內存。
而在此時:
對象析構函數已被調用(資源已清理)。
內存仍被 weak_ptr 的控制塊管理,雖然未釋放,但程序仍能通過 weak_ptr 的機制感知到內存狀態(tài)。
當所有 weak_ptr 銷毀后,內存會被正確釋放。

enable_shared_from_this 和 shared_from_this()

解決場景:

當一個對象已被 shared_ptr 管理時,若在成員函數中直接用 this 創(chuàng)建新的 shared_ptr,會導致 多個獨立的引用計數 。當某一 shared_ptr 析構時,對象可能被提前銷毀,引發(fā)懸空指針或雙重釋放。

問題場景示例1

class BackgroundWorker {
public:
    void startWork() {
        // 錯誤:裸指針 this 跨線程傳遞,主線程可能提前釋放對象
        std::thread([this]() {
            doWork(); // 可能訪問已銷毀對象!
        }).detach();
    }

    void doWork() { /* 自己實現(xiàn)的操作 */ }
};

int main() {
    auto worker = std::make_shared<BackgroundWorker>();
    worker->startWork();
    worker.reset(); // 主線程釋放對象
    // 子線程仍在執(zhí)行 doWork(),this 已失效!
}

enable_shared_from_this 內部保存一個 weak_ptr,指向與對象關聯(lián)的 shared_ptr 控制塊。通過 shared_from_this() 方法生成與已有 shared_ptr 共享所有權的 shared_ptr 確保引用計數一致。

解決示例
通過獲取與 shared_ptr 共享所有權的智能指針,延長對象生命周期

class BackgroundWorker : 
public std::enable_shared_from_this<BackgroundWorker> {
public:
    void startWork() {
        // 正確:通過 shared_from_this() 傳遞所有權
        std::thread([self = shared_from_this()]() {
            self->doWork(); // 安全:self 確保對象存活
        }).detach();
    }
    // ...
};

問題場景示例2:

class TreeNode {
public:
    void addChild() {
        // 創(chuàng)建一個子節(jié)點 child,用 shared_ptr 管理
        auto child = std::make_shared<TreeNode>();
        
        // 錯誤:直接用 this 創(chuàng)建新的 shared_ptr,賦值給 child->parent
        //child->parent引用計數也是 1
        child->parent = std::shared_ptr<TreeNode>(this); // 危險!
        
        // 將 child 添加到當前節(jié)點的 children 列表中
        children.push_back(child);
    }

    std::shared_ptr<TreeNode> parent;  // 父節(jié)點用 shared_ptr 管理
    std::vector<std::shared_ptr<TreeNode>> children; // 子節(jié)點列表
};

int main() {
    // 創(chuàng)建一個根節(jié)點 root,用 shared_ptr 管理
    auto root = std::make_shared<TreeNode>();//root引用計數是 1
    root->addChild(); // 調用 addChild,導致雙重釋放!
}

這段代碼的關鍵問題出現(xiàn)在 child->parent = std::shared_ptr(this)
this 是當前 TreeNode 對象的裸指針(即 root 指向的對象)。
直接用 this 創(chuàng)建了一個新的 shared_ptr,賦值給 child->parent。這個新的 shared_ptr 與 root 是 ?完全獨立 的,它不知道 root 的存在,因此它的引用計數 “也是 ?1”。

當 main 函數結束時,root 的引用計數減為 ?0,會釋放它指向的 TreeNode 對象。

但此時 child->parent 也指向同一個 TreeNode 對象,且它的引用計數仍然是 ?1?(如果 child 未被銷毀)。當 child 析構時,child->parent 的引用計數減為 ?0,會再次嘗試釋放同一個 TreeNode 對象,導致** ?雙重釋放**。

解決示例:

class TreeNode : public std::enable_shared_from_this<TreeNode> {
public:
    void addChild() {
        auto child = std::make_shared<TreeNode>();
        
        // 通過 shared_from_this() 獲取與 root 共享所有權的 shared_ptr
        child->parent = shared_from_this(); // 引用計數 +1
        
        children.push_back(child);
    }

    std::shared_ptr<TreeNode> parent;
    std::vector<std::shared_ptr<TreeNode>> children;
};

int main() {
    auto root = std::make_shared<TreeNode>();
    root->addChild(); // 安全:root 的引用計數變?yōu)?2
}

shared_ptr的線程安全問題

http://m.fzitv.net/article/41353.htm

到此這篇關于C++ unique_ptr、shared_ptr、weak_ptr的區(qū)別小結的文章就介紹到這了,更多相關C++ unique_ptr shared_ptr weak_ptr內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

鄄城县| 嘉义县| 孝感市| 垦利县| 木里| 织金县| 宁德市| 阜宁县| 信宜市| 离岛区| 和平区| 麻城市| 浦东新区| 西林县| 玛纳斯县| 镇巴县| 阿勒泰市| 民乐县| 旬邑县| 西乡县| 福建省| 永登县| 福贡县| 会东县| 大庆市| 汾阳市| 永安市| 瓮安县| 岳阳市| 江津市| 常宁市| 栾川县| 鸡东县| 翁源县| 特克斯县| 鄱阳县| 诸暨市| 合川市| 玉山县| 普格县| 元阳县|