C++智能指針萬字詳細講解(包含智能指針的模擬實現)

? ? ? ? 在筆試,面試中智能指針經常出現,如果你對智能指針的作用,原理,用法不了解,那么可以看看這篇博客講解,此外本博客還簡單模擬實現了各種指針,在本篇的最后還應對面試題對智能指針的知識點進行了拓展。希望能加深你對智能指針的理解。那么開始學習吧!

一.智能指針作用

? ? ? ? C++的智能指針主要作用是為了防止內存泄漏。在代碼中我們new出來的對象都需要delete,但是當我們我們忘記或者代碼出現異常導致沒有delete對象,就會產生內存泄漏。長期運行的程序出現內存泄漏,影響很大,如操作系統、后臺服務等等,出現內存泄漏會導致響應越來越慢,最終卡死。

? ? ? ? 下面我們看看一個常見的因為異常導致內存泄漏的例子:

#include<iostream>
using namespace std;static int sa = 1;
int div_func(int a, int b)
{if (b == 0){throw invalid_argument("除0錯誤");}return a / b;
}
int Func1()
{int* a1 = new int{1};int* a2 = new int{sa};int n=div_func(*a1,*a2);sa--;delete a1;delete a2;return n;
}int main()
{try{while (1){int n = Func1();cout << n;}}catch (exception& e){cout << e.what() << endl;}return 0;
}

運行結果:

? ? ? ? main函數調用func1函數,func中new出了a1,和a2,然后調用div_func,當b=0,此時就會拋出異常,異常被mian函數捕獲直接跳轉,此時new出來的a1和a2就不會被delete,導致內存泄漏。這種代碼的內存泄漏,還是比較難防備,此時就需要使用智能指針。

二.智能指針原理?

? ? ? ? 我們首先介紹一下什么是RAII。

????????RAII(Resource Acquisition Is Initialization)(資源獲取即初始化)是一種利用對象生命周期來控制程序資源(如內存、文件句柄、網絡連接、互斥量等等)的簡單技術。在對象構造時獲取資源,接著控制對資源的訪問使之在對象的生命周期內始終保持有效,最后在對象析構的時候釋放資源。借此,我們實際上把管理一份資源的責任托管給了一個對象。

????????這種做法有兩大好處:

  • 1.不需要顯式地釋放資源。
  • 2.對象所需的資源在其生命期內始終保持有效。

????????智能指針也就是利用RAII的原理實現的,把管理一份資源的責任托管給了一個對象,通過構造函數獲取資源,通過析構函數釋放資源,看代碼:

template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr):_ptr(ptr){}~SmartPtr(){if (_ptr)delete _ptr;}
private:T* _ptr;
};

? ? ? ? 這樣我們就能通過類的生命周期來對資源進行管理和釋放。對于最開始的代碼我們只需要把?int* a1 = new int{1};int* a2 = new int{sa}代碼寫成SmartPtr<int > sp1=new{1};SmartPtr<int >sp2=new{sa},這樣即使因為拋異常跳轉到mian()函數也會因為生命周期的結束自動釋放資源。上面的代碼就是智能指針的基本原理。

三.智能指針介紹和使用?

C++常見的智能指針有三種,這里我們只做基本介紹和使用,詳細特點我們后面實現再介紹。

1.std::unique_ptr

特點:獨占資源所有權,不可復制(不能進行拷貝構造和賦值運算符重載)但支持移動語義,生命周期結束時自動釋放資源,保證只有一個對象只有一個unique_ptr指針,避免重復析構。

class A
{
public:int a;A(int n){a = n;}~A(){std::cout << "調用析構" << std::endl;}};
int main()
{std::unique_ptr<A> ptr = std::make_unique<A>(1);//c++高版本寫法。std::unique_ptr<A> ptr( new A(1));//第二種寫法//std::unique_ptr<A> ptr1=ptr;//禁止了拷貝構造會報錯std::unique_ptr<A> ptr2 = std::move(ptr);  // 所有權轉移
}

2.std::shared_ptr

特點:共享資源所有權,通過引用計數管理生命周期,線程安全的引用計數更新。允許復制。

每復制一個shared_ptr,計數+1,析構一個計數-1,計數為零才調用析構。

class A
{
public:int a;A(int n){a = n;}~A(){std::cout << "調用析構" << std::endl;}};
int main()
{std::shared_ptr<A> ptr = std::make_shared<A>(1);std::shared_ptr<A> ptr1 = ptr;}

3.std::weak_ptr

  • ?特點:弱引用,不影響?shared_ptr?的引用計數,需通過?lock()?提升為?shared_ptr?訪問資源(后面詳細講解)。

四.簡單模擬實現std::auto_ptr

? ? ? ? auto_ptr主要是在早期版本的C++使用,現在基本不會使用,特點是轉移管理權,即當指針復制時,讓新指針指向舊指針,再將舊指針指向空,我們主要做個了解。

namespace bit
{template<class T>class auto_ptr{public:auto_ptr(T* ptr = nullptr):_ptr(ptr){}//移交管理權auto_ptr(auto_ptr<T>& ap){_ptr = ap.get();ap._ptr = nullptr;}//釋放原來的,接收管理權auto_ptr<T>& operator=(auto_ptr<T>& ap){if (this != &ap){// 釋放當前對象中資源if (_ptr)delete _ptr;}_ptr=ap.get();ap.ptr=null;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~auto_ptr(){delete _ptr;}T* get(){return _ptr;}private:T* _ptr;};}class Date
{
public:Date(int year,int month,int day){_year = year;_month = month;_day = day;cout << "調用構造函數" << endl;}~Date(){cout << "調用析構" << endl;}int _year;int _month;int _day;
};
int main()
{bit::auto_ptr<int> ap1 = new int{ 1 };bit::auto_ptr<Date> ap2 = new Date{ 1,1,1 };cout << *ap1 << endl;cout << ap2->_year << endl;bit::auto_ptr<Date> ap3 = ap2;}

運行結果:

最后一行時的監視窗口:

? ? ? ? auto_ptr作為智能指針,當調用拷貝構造或賦值運算符重載,不允許多個智能指針指向同一個對象,而是將一個智能指針的資源管理權移交給另外一個智能指針,這種做法是不太好的,這意味著,賦值后的原auto_ptr對象將不再擁有指針的所有權,其內部指針會被置為NULL。這種行為可能導致一些潛在的錯誤,因為程序員可能期望原對象仍然擁有指針的所有權。很多公司明確要求不能使用auto_ptr。

五.簡單模擬實現std::unique_ptr

????????unique_ptr的實現原理:簡單粗暴的防拷貝,下面簡化模擬實現了一份UniquePtr來了解它的原理。

	template<class T>class unique_ptr{public:unique_ptr(T* ptr)	:_ptr(ptr){}unique_ptr (unique_ptr& up) = delete;unique_ptr<T>& operator=(unique_ptr& up) = delete;T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}private:T* _ptr;};

? ? ? ? 通過刪除拷貝構造函數和賦值運算符重載來確保指向該資源的只有該智能指針。也就是說是一個資源只能有一個智能指針。

六.簡單模擬實現std::shared_ptr

? ? ? ?上面的倆種指針之所以只能做到一個資源只能有一個智能指針,是因為沒有解決多個智能指針指向一份資源從而導致重復析構的問題而shared_ptr可以解決這個問題

????????shared_ptr的原理:是通過引用計數的方式來實現多個shared_ptr對象之間共享資源所有智能指針都指向同一個內存和引用計數。

? ? ? ? 當有智能指針指向內存資源時,同時讓共享的引用計數++,當智能指針析構時,只是讓引用計數--,只有當引用計數為0時再調用指向資源的析構函數。此外為了多線程訪問,對計數需要加鎖保護。

? ? ? ? 具體看代碼:

namespace bit
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx){AddRef();}void Release(){_pmtx->lock();bool flag = false;if (--(*_pRefCount) == 0 && _ptr){cout << "delete:" << _ptr << endl;delete _ptr;delete _pRefCount;flag = true;}_pmtx->unlock();if (flag == true){delete _pmtx;}}void AddRef(){_pmtx->lock();++(*_pRefCount);_pmtx->unlock();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){//if (this != &sp)if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pRefCount = sp._pRefCount;_pmtx = sp._pmtx;AddRef();}return *this;}int use_count(){return *_pRefCount;}~shared_ptr(){Release();}// 像指針一樣使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}private:T* _ptr;int* _pRefCount;mutex* _pmtx;};

? ? ? ? ?必須注意的是多線程的話,因為所有指針共享一個引用計數,對引用計數必須加鎖訪問,這里我們只做簡單模擬。

七.weak_ptr的模擬實現

? ? ? ? weak_ptr有2個作用:

  • ?打破循環引用:通過將類成員聲明為?weak_ptr,避免?shared_ptr?的循環引用導致內存泄漏
  • ?安全訪問資源:通過?lock()?方法原子性地獲取?shared_ptr,若對象已釋放則返回空指針,避免懸垂指針

? ? ? ? 下面我們引入第一個作用:?打破循環引用

? ? ? ? 在shard_ptr中看似很安全,但是可能會出現循環引用的問題,下面讓我們看看

class B;
class A {
public:std::shared_ptr<B> b_ptr;  // 強引用int a;~A() { std::cout << "A destroyed\n"; }
};
class B {
public:std::shared_ptr<A> a_ptr;  // 強引用int b;~B() { std::cout << "B destroyed\n"; }
};int main(){auto ptr_A = std::make_shared<A>();//c++新版本創建智能指針的新方法auto ptr_B = std::make_shared<B>();ptr_A->b_ptr = ptr_B;   //ptr_A的引用計數++;ptr_B->a_ptr = ptr_A;  //ptr_B的引用計數++  循環引用,引用計數均為2Bstd::cout << "運行完畢" << std::endl;
}  // mian結束,智能指針ptr_A,ptr_B引用計數只能減為1,對象未銷毀

運行結果:

?????????可以看到我們并未成功調用對象A和B的析構函數,造成了內存泄漏。我們來分析一下原因。

????????首先ptr_A指向A對象(假設為a),ptr_A的計數為1,ptr_B指向B對象(假設為b),ptr_B的計數也為1,然后?ptr_A->b_ptr = ptr_B; ptr_B->a_ptr = ptr_A; 此時ptr_A和ptr_B的計數增加為2.

????????我們來畫圖理解。

? ? ? ? 這里我們用控制塊A和控制B代表指向A 和B的計數 ,當程序運行結束ptr_A,ptr_B調用析構時,計數都減少1,如下:

? ? ? ? ?此時指向A和B對象的計數都為1,無法自動調用析構,造成內存泄漏(new 出來的對象也是不會自動調用析構的)。

? ? ? ? 我們要知道shared_ptr智能指針計數為0時才能調用指向對象的析構函數。

????????為了解決上面的問題,我們創鍵了weak_ptr.

? ? ? ? weak_ptr一般和shard_ptr搭配使用,weak_ptr可以接受shard_prt類型的指針,但是不會影響計數。也就是說weak_ptr的構造和析構都不會增加和減少計數,同時weak_ptr也不會計數為0也不會調用指向對象的析構函數,只是充當指向作用

? ? ? ? 我們使用weak_ptr對上面的代碼進行修改。

class B;
class A {
public:std::weak_ptr<B> b_ptr;  // 強引用int a;~A() { std::cout << "A destroyed\n"; }
};
class B {
public:std::shared_ptr<A> a_ptr;  // 強引用int b;~B() { std::cout << "B destroyed\n"; }
};int main()
{auto ptr_A = std::make_shared<A>();auto ptr_B = std::make_shared<B>();ptr_A->b_ptr = ptr_B;ptr_B->a_ptr = ptr_A;std::cout << "運行完畢";
}

運行結果如下:

? ? ? ? ?我們將對象A的智能指針替換為weak_ptr,其他不變。再來分析析構過程

?????????首先ptr_A指向A對象,ptr_B指向B對象,計數都為1。ptr_A->b_ptr = ptr_B,此時A中的是weak_ptr<B>指針,不會增加ptr_B的計數,而?ptr_B->a_ptr = ptr_A,會增加ptr_A的計數(對哪個指針進行拷貝就是增加哪個指針的計數)。ptr_A計數為2,ptr_B計數為1。此時情況如圖:

????????當main結束時ptr_A調用自己的析構函數計數減少為1, 同時ptr_B自動調用自己的析構函數,計數減少為0。

????????由于ptr_B計數為0需要調用b的析構函數調用b的析構函數釋放資源后要調用成員變量a_ptr的析構函數(析構函數的順序是先調用自己的,再調用成員變量的),此時控制塊A計數為1(a_ptr是拷貝ptr_A的,倆者計數相同),減一后為0,計數為零需要調用a的析構函數,a的成員變量b_ptr計數為0,A可以直接析構,到此對象全部成功析構。圖示如下:

????????

? ? ? ? 因此析構函數的調用順序是先B后A,但是B對象是后于A對象被銷毀的。?

? ? ? ? 上面這么多就是為了論證weak_ptr的一個作用:?打破循環引用,避免內存泄漏。

????????對于weak_ptr的第二個作用就好理解多了。?

  • 安全訪問資源:通過?lock()?方法原子性地獲取?shared_ptr,若對象已釋放則返回空指針,避免懸垂指針。
std::weak_ptr<int> wp;
if (auto sp = wp.lock()) {  // 檢查對象是否存在// 安全使用 sp
}

? ? ????wp.lock()是看計數是否為0,為0返回空,不為零返回一個shared_ptr(計數也會++)。

????????以上這些講講的都是weak_ptr的作用和原理,下面我們給出weak_ptr的模擬實現。

template <typename T>
class WeakPtr {
public:// 默認構造函數(空指針)WeakPtr() : ptr_(nullptr), ref_count_(nullptr) {}// 從 SharedPtr 構造template <typename U>WeakPtr(const SharedPtr<U>& shared) : ptr_(shared.ptr), ref_count_(shared.ref_count) {}// 拷貝構造函數WeakPtr(const WeakPtr& other) : ptr_(other.ptr_), ref_count_(other.ref_count) {}shared_ptr<T> lock() const {if (*ptr<=0) return shared_ptr<T>();return shared_ptr<T>(ptr, ref_count_);}// 析構函數~WeakPtr() {}T& operator*(){return *ptr;}T* operator->(){return ptr;}T* get() const{return ptr;}private:T* ptr;                        // 指向對象的指針int* ref_count_;           // 指向控制塊的指針// 允許 SharedPtr 訪問私有成員template <typename U>friend class SharedPtr;
};

? ? ? ? ? ? 要注意的這里只是簡單實現,并不詳細。

八.面試題拓展

????????上面的講解基本就能解決絕大多數的面試題了,但是面試的知識點也越來越細了,因此我們再根據常見的面試題進行拓展。

weak_ptr真的不計數?是否有計數方式?在哪分配的空間?

? ? ? ??

對于1,2小問,這里我們需要介紹一下share_ptr和weak_ptr中控制塊的概念。

????????控制塊是智能指針實現引用計數機制的核心數據結構,包含以下信息

  1. ?強引用計數(use_count)?:記錄當前有多少個shared_ptr持有對象。
  2. ?弱引用計數(weak_count)?:記錄當前有多少個weak_ptr觀察對象。
  3. ?對象指針:指向實際管理的對象(可能為空,若對象已被銷毀)。
  4. ?自定義刪除器(可選)。

?????也就是說shared_ptr和weak_ptr中有2個強弱倆個計數,其中強引用計數作用就是當計數為0時調用指向對象的析構函數,但控制塊仍存在,弱引用作用是當?弱引用歸零時控制塊本身被釋放。

那么控制塊的作用是什么:

?1. 支持weak_ptr的安全操作

  • ?感知對象狀態:即使對象已被銷毀(強引用歸零),weak_ptr仍需通過控制塊判斷對象是否有效(如lock()要通過控制塊判斷);
  • ?避免懸空控制塊:若控制塊隨對象一起釋放,weak_ptr將無法判斷對象是否存在,導致未定義行為

?2. 避免控制塊內存泄漏

  • ?生命周期分離:控制塊的存活由弱引用計數決定。即使對象已銷毀,只要存在weak_ptr觀察,控制塊就必須保留以記錄弱引用信息
  • ?最終釋放機制:當所有weak_ptr銷毀(弱引用歸零),控制塊才會被釋放,避免內存殘留

這里我們在總結一下智能指針的釋放流程。

釋放流程

  1. ?對象銷毀:當最后一個shared_ptr析構時,強引用計數歸零,對象被釋放。
  2. ?控制塊保留:若仍有weak_ptr觀察(弱引用計數>0),控制塊繼續存在。
  3. ?控制塊釋放:當所有weak_ptr析構(弱引用歸零),控制塊被銷毀

?對于最后1個小問,我們還需要了解控制塊的分配方式

  • ?new分配:直接使用new時,對象和控制塊分兩次分配,控制塊獨立存在
  • ?make_shared優化:通過make_shared創建shared_ptr時,對象和控制塊分配在同一塊連續內存中,減少內存碎片和分配次數。因此第二種方法更好一點。

因此上面面試題的答案是:

weak_ptr真的不計數?是否有計數方式,在哪分配的空間。

計數,控制塊中有強弱引用計數,如果是使用make_shared初始化的函數則它所在的控制塊空間是在所引用的shared_ptr中同一塊的空間,若是new則控制器所分配的內存與shared_ptr本身所在的空間不在同一塊內存。

??好了,智能指針就講解到這了,感覺有幫助的話,請點點贊吧,這真的很重要。

????????????????????????????????

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74656.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74656.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74656.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Go】Go語言結構體筆記

整體介紹 雖然 Go 語言不是傳統意義上的面向對象語言&#xff0c;但它提供了結構體&#xff08;struct&#xff09;來組織數據&#xff0c;并且可以為結構體綁定方法&#xff0c;從而達到面向對象的部分效果。 關鍵知識點包括&#xff1a; 結構體定義與實例化 定義結構體時使用…

Three.js 快速入門教程【十八】射線拾取模型——鼠標點擊屏幕選中模型或物體

系列文章目錄 Three.js 快速入門教程【一】開啟你的 3D Web 開發之旅 Three.js 快速入門教程【二】透視投影相機 Three.js 快速入門教程【三】渲染器 Three.js 快速入門教程【四】三維坐標系 Three.js 快速入門教程【五】動畫渲染循環 Three.js 快速入門教程【六】相機控件 Or…

Object.defineProperty()Proxy詳解(Vue23數據劫持實現)

底層原理&#x1f447;&#x1f3ff; 總結一下&#xff0c;結構應該包括&#xff1a; 1. 方法的基本作用和參數。 2. 數據描述符和存取描述符的區別。 3. 屬性定義的內部處理流程。 4. 在Vue中的應用實例。 5. 常見錯誤和正確實踐。 每個部分都要結合搜索結果的信息&…

MySQL 進階語法:函數、約束、多表查詢、事務

目錄 一、MySQL 常用函數 1. 字符串函數 1.1 基本字符串操作 1.2 字符串截取與處理 1.3 字符串搜索與替換 2. 數值函數 2.1 基本數學運算 2.2 數學計算 2.3 隨機數與符號 3. 日期時間函數 3.1 獲取當前時間 3.2 日期時間計算 3.3 日期時間提取 3.4 日期時間格式化…

第 12 章(番外)| Solidity 安全前沿趨勢 × 審計生態 × 職業路徑規劃

&#x1f310; 第 12 章&#xff08;番外&#xff09;| Solidity 安全前沿趨勢 審計生態 職業路徑規劃 ——做得了審計&#xff0c;也接得了項目&#xff0c;走進 Web3 安全工程師的職業實戰地圖 ? 本章導讀 Solidity 安全&#xff0c;不只是代碼安全、業務安全、審計安全…

1、pytest基本用法

目錄 先給大家分享下學習資源 1. 安裝pytest 2. 編寫用例規則 3. 執行用例 最近在學習pytest的用法 并且用這套框架替換了原來的unittest&#xff0c; 同是測試框架 確實感覺到pytest更加便捷 這邊分享給大家我得學習心得 先給大家分享下學習資源 1 官方文檔 pytest 官方…

【sylar-webserver】5 協程調度模塊

文章目錄 設計思路三種協程的切換 協程調度模塊&#xff0c;需要把前面的線程模塊和協程模塊結合使用 ~ 設計思路 構造函數定義 線程池 基本信息。start()&#xff0c;創建線程池&#xff0c;每個線程創建都執行 run()。每個線程在 run() 里&#xff0c;查找任務隊列 m_tasks…

Go 語言規范學習(1)

文章目錄 IntroductionNotation示例&#xff08;Go 語言的 if 語句&#xff09;&#xff1a; Source code representationCharacters例子&#xff1a;變量名可以是中文 Letters and digits Lexical elementsCommentsTokensSemicolons例子&#xff1a;查看程序所有的token Ident…

探索抓包利器ProxyPin,實現手機APP請求抓包,支持https請求

以下是ProxyPin的簡單介紹&#xff1a; - ProxyPin是一個開源免費HTTP(S)流量捕獲神器&#xff0c;支持 Windows、Mac、Android、IOS、Linux 全平臺系統- 可以使用它來攔截、檢查并重寫HTTP(S)流量&#xff0c;支持捕獲各種應用的網絡請求。ProxyPin基于Flutter開發&#xff0…

深度學習3-pytorch學習

深度學習3-pytorch學習 Tensor 定義與 PyTorch 操作 1. Tensor 定義&#xff1a; Tensor 是 PyTorch 中的數據結構&#xff0c;類似于 NumPy 數組。可以通過不同方式創建 tensor 對象&#xff1a; import torch# 定義一個 1D Tensor x1 torch.Tensor([3, 4])# 定義一個 Fl…

深入淺出Spring-Boot-3.x.pdf

通過網盤分享的文件&#xff1a;深入淺出Spring-Boot-3.x.pdf 鏈接: https://pan.baidu.com/s/10ZkhmeIXphEwND9Rv4EBlg?pwduatm 提取碼: uatm

springboot啟動事件CommandLineRunner使用

什么是CommandRunner CommandRunner是springboot啟動完成時會調用的一個runner 啟動參數會傳遞到這個runner 我們能用來做一些初始化工作和緩存預熱等工作 ApplicationRunner VS CommandRunner? 這兩個Runner作用一樣 只是得到的啟動參數格式不一樣 前者是一個Argument對象…

數據可視化TensorboardX和tensorBoard安裝及使用

tensorBoard 和TensorboardX 安裝及使用指南 tensorBoard 和 TensorBoardX 是用于可視化機器學習實驗和模型訓練過程的工具。TensorBoard 是 TensorFlow 官方提供的可視化工具&#xff0c;而 TensorBoardX 是其社區驅動的替代品&#xff0c;支持 PyTorch 等其他框架。以下是它…

藍橋杯C++基礎算法-多重背包

這段代碼實現了一個多重背包問題的動態規劃解法。多重背包問題與完全背包問題類似&#xff0c;但每個物品有其數量限制。以下是代碼的詳細思路解析&#xff1a; 1. 問題背景 給定 n 個物品&#xff0c;每個物品有其體積 v[i]、價值 w[i] 和數量 s[i]&#xff0c;以及一個容量為…

【SUNO】【AI作詞】【提示詞】

仿寫歌詞提示詞模板&#xff08;升級版&#xff09; 一、仿寫目標 風格定位 音樂風格&#xff1a; [填寫目標風格&#xff0c;如&#xff1a;民謠/流行/古風/電子/爵士等]參考案例&#xff1a;如《成都》的敘事民謠&#xff0c;《孤勇者》的勵志流行。 情感基調&#xff1a; […

26考研——樹與二叉樹_樹與二叉樹的應用(5)

408答疑 文章目錄 三、樹與二叉樹的應用哈夫曼樹和哈夫曼編碼哈夫曼樹的定義概念帶權路徑長度&#xff08;WPL&#xff09;計算示例分析 哈夫曼樹的構造算法描述哈夫曼樹的性質示例 哈夫曼編碼Huffman樹的編碼規則Huffman樹的構建過程前綴編碼前綴編碼的分析及應用 Huffman樹的…

【VUE】day06 動態組件 插槽 自定義指令 ESlint

【VUE】day06 動態組件 & 插槽 & 自定義指令 1. 動態組件1.1 通過不同的按鈕展示不同的組件1.1.1回顧click 1.2 keep-alive的使用1.3 keep-alive對應的生命周期函數1.3.1 keep-alive的include屬性1.3.2 exclude 1.4 組件注冊名稱和組件聲明時name的區別1.4.1 組件聲明時…

nodejs-原型污染鏈

還是老規矩&#xff0c;邊寫邊學&#xff0c;先分享兩篇文章 深入理解 JavaScript Prototype 污染攻擊 | 離別歌 《JavaScript百煉成仙》 全書知識點整理-CSDN博客 Ctfshow web入門 nodejs篇 web334-web344_web334 ctfshow-CSDN博客 334-js審計 var express require(expr…

Oracle 數據庫通過exp/imp工具遷移指定數據表

項目需求&#xff1a;從prod數據庫遷移和復制2個表(BANK_STATE&#xff0c;HBS)的數據到uat數據庫環境。 數據庫版本&#xff1a;Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 遷移工具&#xff1a;客戶端exp/imp工具 -- 執行命令 從Prod數據庫導出數據exp us…

企業級基于SpringBoot的MQTT的構建和使用

基于SpringBoot的MQTT配置及使用 首先要使用EMQX搭建一個MQTT服務器&#xff0c;參考文檔&#xff1a;EMQX快速開始 本著開源分享的觀點&#xff0c;閑話不多說&#xff0c;直接上代碼 導入Maven <dependency><groupId>org.springframework.integration</gro…