C++中的智能指針(1):unique_ptr

一、背景

普通指針是指向某塊內存區域地址的變量。如果一個指針指向的是一塊動態分配的內存區域,那么即使這個指針變量離開了所在的作用域,這塊內存區域也不會被自動銷毀。動態分配的內存不進行釋放則會導致內存泄漏。

如果一個指針指向的是一塊已經被釋放的內存區域,那么這個指針就是懸空指針。使用懸空指針會造成不可預料的后果。

如果我們定義了一個指針但未初始化使其指向有效的內存區域時,這個指針就成了野指針。使用野指針訪問內存一般會造成段錯誤,即segmentation fault.

注:Segmentation fault:當程序試圖訪問未被分配的內存或無權訪問的內存區域時,操作系統強制終止程序產生的錯誤。常見原因有:1.解引用空指針(上面所提到的);2.訪問已釋放的內存(懸空指針);3.數組越界;4.棧溢出(無限遞歸或過大的局部變量)。

使用智能指針可以有效避免上述錯誤的發生。智能指針是一個對象,它封裝了一個指向另一個對象的指針。當智能指針對象離開了作用域后,會被自動銷毀。銷毀過程中會調用析構函數刪除所封裝的對象

二、unique_ptr

unique_ptr與它所管理的動態對象是一對一的關系,換言之,不能有兩個unique_ptr對象同時指向相同的一塊地址。

創建一個unique_ptr對象有兩個方法:

unique_ptr<T> ptr1(new T(參數))
unique_ptr<T> ptr1= make_unique<T>(參數)

這里的T是一個類名。方法一:在構造函數中傳入new分配的類對象。方法二:使用make_unique函數,它的參數是類T的構造函數的參數。

我們來看一個例子:

class Person
{
private:string name;int age;public:Person(string m_name, int m_age) :name(m_name), age(m_age){}~Person(){cout << "對象"<<name<<"被釋放" << endl;}void check(){if (age < 18){cout << name << "的年齡為" << age << ",小于18歲" << endl;}else{cout << name << "的年齡為" << age << ",是成年人" << endl;}}
};int main()
{unique_ptr<Person>ptr1(new Person("比企谷", 17));unique_ptr<Person>ptr2 = make_unique<Person>("伊蕾娜", 18);ptr1->check();ptr2->check();return 0;
}

在main函數中我們定義了兩個unique_ptr指針ptr1和ptr2,分別封裝了兩個動態創建的Person對象“比企谷”和“伊蕾娜”。由于智能指針重載了間接成員運算符和解引用運算符,它們會返回智能指針所包含對象的指針或者引用,因此可以像使用普通指針那樣使用智能指針。例如上面的ptr1->check(),直接調用了Person對象的成員check.智能指針離開作用域后自動銷毀,并調用析構函數釋放指向的Person對象。

需要注意,下面三種情況也會刪除當前所管理的對象:

ptr1=nullptr;
ptr1=move(ptr2);
ptr1.reset(new Person("雪之下雪乃",17));

上文說過,unique_ptr對所管理的資源具有獨占性,所以unique_ptr的一個重要特性就是不能被拷貝也不能被賦值。

下面這段代碼會在編譯時出錯:

unique_ptr<Person>ptr3=ptr2;

unique_ptr的類定義中沒有這個拷貝構造函數。這樣就保證了unique_ptr對象封裝的指針不能和其它unique_ptr共用。但是上文提到了,我們可以對unique_ptr所管理對象的所有權進行轉移,即:使用move函數。

unique_ptr<Person>ptr3=move(ptr2)

這樣ptr3就擁有了原來ptr2所封裝的指針的控制權。此時ptr2只包含了一個空指針,可以使用ptr3來訪問它所封裝的對象的成員。由于ptr2只包含了一個空指針,如果還使用ptr2訪問成員,會出現segmentation fault.

再來看一個例子:

class energy
{
public:energy(){cout << "能量已充滿" << endl;}~energy(){cout << "能量值為0" << endl;}
};unique_ptr<energy> fill()
{return unique_ptr<energy>(new energy());
}void consume(unique_ptr<energy>Energy)
{cout << "能量被消耗了" << endl;
}int main()
{cout << "開始" << endl;auto eng = fill();consume(eng);//錯誤的cout << "結束" << endl;
}

為什么consume(eng)是錯誤的?因為沒有相應的拷貝構造函數,所以不能直接傳值。正確方法如下:

consume(move(eng));

使用move函數將所有權轉移給新的unique_ptr對象。

結果如下:

可以看到,這個engery對象被自動釋放。

再來看一個例子:

struct Packet
{long m_id;char data[1000];Packet(long id) :m_id(id) {};
};struct Compare
{bool operator()(const Packet& a, const Packet& b){return a.m_id < b.m_id;}
};void sort_value_vector(int n)
{vector<Packet>vec;for (int i = 0; i < n; i++){vec.push_back(Packet(rand() % n));}sort(vec.begin(), vec.end(), Compare());
}

對于Packet這種較大的對象,排序意味著需要大量數據的移動復制。因為一個Packet對象大概是1008個字節,每交換兩個Packet對象就需要復制1008字節的數據。我們可以將容器中的對象改成指針,這樣排序的時候只涉及到指針值的復制。(交換兩個指針只需復制4/8個字節)

struct Packet
{long m_id;char data[1000];Packet(long id) :m_id(id) {};
};struct Compare
{bool operator()(const Packet* pa, const Packet* pb){return pa->m_id < pb->m_id;}
};void sort_value_vector(int n)
{vector<Packet*>vec;for (int i = 0; i < n; i++){vec.push_back(new Packet(rand() % n));}sort(vec.begin(), vec.end(), Compare());
}

但是對于指針,需要單獨進行維護。刪除替換時需要釋放不再使用的指針對象。不妨把容器中的指針換成unique_ptr,則不僅獲得了普通指針的性能,還實現了內存資源的自動釋放。

struct Packet
{long m_id;char data[1000];Packet(long id) :m_id(id) {};
};struct Compare
{template<template<typename> typename ptr>bool operator()(const ptr<Packet>&pa, const ptr<Packet>&pb){return pa->m_id < pb->m_id;}
};template<typename ptr>
void sort_value_vector(int n)
{vector<ptr>vec;for (int i = 0; i < n; i++){vec.push_back(new Packet(rand() % n));}sort(vec.begin(), vec.end(), Compare());
}

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

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

相關文章

HTTPS安全機制:從加密到證書全解析

目錄 1.HTTPS是什么 2.加密是什么 3.HTTPS的加密過程 3.1對稱加密 3.2非對稱加密 4.引入證書 4.1"中間人"攻擊 4.2 引入證書機制 4.3 理解數據簽名 4.4 非對稱加密 對稱加密 證書認證 5.常見問題 5.1 Fiddler等抓包工具&#xff0c;為啥能解析HTTPS的數據…

2024年深度學習技術主要發展分析

摘要&#xff1a;深度學習作為人工智能領域的戰略級技術&#xff0c;在2024年持續取得突破性進展&#xff0c;持續重構現代戰爭規則&#xff0c;成為大國軍事智能化競爭的核心角力點。對2024年深度學習技術熱門領域的主要發展進行了綜合評述。研究了深度學習技術的發展現狀&…

Swift 枚舉:深入理解與高效使用

Swift 枚舉:深入理解與高效使用 引言 Swift 枚舉(Enum)是 Swift 編程語言中的一種基本數據類型,它允許我們將一組相關的值組合在一起。枚舉在 Swift 中有著廣泛的應用,從簡單的數據分類到復雜的業務邏輯處理,枚舉都能發揮巨大的作用。本文將深入探討 Swift 枚舉的原理、…

從大模型到云游戲,國鑫SY8108G-G4如何化身“全能AI引擎”?

當大模型參數量突破萬億級&#xff0c;傳統服務器在散熱枷鎖與擴展瓶頸前舉步維艱。國鑫全新推出的 SY8108G-G4 8U8卡AI服務器 &#xff0c;以顛覆性架構支持8張600W GPU全速并行&#xff0c;結合CPU-GPU直連、冗余電源和彈性擴展三大優勢&#xff0c;為AI訓練、生成式創作、數…

在多個DHCP服務器的網絡環境中選擇指定的DHCP服務

問題 學校有兩個網絡&#xff0c;我電腦網線插在同一個交換機的同一個接口上&#xff0c;有時候獲取的是172.27開頭的IP&#xff0c;有時候獲取的是192.168開頭的IP。 通常第一次開機獲取的是172.27的IP&#xff0c;插拔網線或重啟網絡接口后會變為192.168的IP。 兩個網絡各有…

【Nginx】實測Nginx增加第三方主動式健康檢查模塊

一、環境說明系統版本&#xff1a;CentOS 7.9內核版本&#xff1a;3.10.0-1160.119.1Nginx版本&#xff1a;1.26.3第三方檢測模塊及版本&#xff1a;nginx_upstream_check_module&#xff08;v0.4.0&#xff0c;兼容nginx 1.20&#xff09;二、nginx安裝部署2.1 下載檢測模塊目…

pytest中mark的使用

在pytest中&#xff0c;mark&#xff08;標記&#xff09;是用于對測試用例進行分類、篩選或附加元數據的重要功能。以下是其核心使用方法&#xff1a; 1. ?基本標記定義與使用? ?注冊標記?&#xff1a;在pytest.ini中預先定義標記&#xff08;避免運行時警告&#xff09;&…

STM32N6--NPU簡單介紹

關鍵詞&#xff1a;STM32N6、生物神經元、神經網絡處理單元&#xff08;NPU&#xff09;、數據流處理 參考鏈接&#xff1a; RM0486 Reference manual STM32N647/657xx Arm-based 32-bit MCUsST_中文論壇【資料合集】STM32N6超全資料合集&#xff08;定期更新&#xff09;B站_…

一款開源免費、通用的 WPF 主題控件包

前言 今天大姚給大家分享一款開源免費&#xff08;MIT License&#xff09;、通用的 WPF 主題控件包&#xff1a;Rubyer WPF。 WPF介紹 WPF是一個強大的桌面應用程序框架&#xff0c;用于構建具有豐富用戶界面的 Windows 應用。它提供了靈活的布局、數據綁定、樣式和模板、動…

windows安裝python環境以及對應編輯器的詳細流程

windows安裝python環境以及對應編輯器的詳細流程 一、安裝 Python 環境 步驟 1&#xff1a;下載 Python 安裝包 訪問 Python 官網&#xff1a;https://www.python.org/downloads/windows/選擇最新穩定版本&#xff08;如 Python 3.12.x&#xff09;&#xff0c;點擊 Download W…

高保真組件庫:下拉多選

制作一個高保真的下拉多選需要具備多種交互事件。 拖拽一個文本框并命名為“下拉文本輸入框”和一個向下的箭頭組合在一起,外觀上看起來是下拉組件。為了美觀調整一些邊框顏色、圓角、文字左邊距等。 拖拽一個矩形作為下拉選項的容器,啟動陰影xy都為0 制作下拉選項:拖拽一個…

sqli-labs靶場通關筆記:第1-4關 聯合注入

第1關&#xff1a;單引號閉合1.這是第1關的界面&#xff0c;讓我們以id作為參數輸入&#xff0c;方式為數值&#xff0c;這里輸入?id1看一下。2.顯示了id1的用戶名和密碼。分析&#xff1a;在sql注入漏洞中&#xff0c;第一步是要尋找注入點&#xff0c;即可以輸入參數的地方&…

和服腰封改造:3種解構主義造型的東方美學新解

和服腰封改造&#xff1a;3種解構主義造型的東方美學新解在東京原宿的小巷里&#xff0c;一場關于和服腰封的"溫柔革命"正在悄然發生。年輕設計師們將傳統寬腰帶拆解重構&#xff0c;創造出既保留東方神韻又充滿當代氣息的造型藝術。正如一位新銳設計師所說&#xff…

什么是強化學習(RL)--3

如果reward大多數情況下都是0&#xff0c;只有少數是很大的值。這種情況下就是稀疏reward的問題。比如你要教機械手臂拴螺絲&#xff0c;只有最后把螺絲栓進去才可以&#xff0c;其余機械手臂的位置都不可以。額外的reward幫agent學習。reward shaping射擊游戲cs,這個游戲中&am…

彩虹云商城全解源碼系統|人工客服系統

核心升級亮點 人工客服系統&#xff1a;新增智能工單在線IM雙模式多端同步&#xff1a;PCH5小程序APP四端數據實時互通支付升級&#xff1a;支持數字人民幣收款安全加固&#xff1a;內置Web應用防火墻(WAF) 部署教程 ? B站視頻教程 包含&#xff1a; 寶塔環境配置&#xf…

川翔云電腦:突破硬件極限,重構設計生產力范式

一、硬核配置&#xff1a;顯存與算力的雙重革命川翔云電腦提供從 RTX 2080 Ti 到 RTX 4090 Plus 的全系列 GPU 機型&#xff0c;其中旗艦級 4090 Plus 單卡配備48GB 超大顯存&#xff0c;較傳統 4090 顯存翻倍&#xff0c;可流暢加載 1200 萬面數的超復雜模型&#xff08;如《黑…

深入解析 TCP 連接狀態與進程掛起、恢復與關閉

文章目錄深入解析 TCP 連接狀態與進程掛起、恢復與關閉一、TCP 連接的各種狀態1. **LISTEN**&#xff08;監聽&#xff09;2. **SYN_SENT**&#xff08;SYN 已發送&#xff09;3. **SYN_RECEIVED**&#xff08;SYN 已接收&#xff09;4. **ESTABLISHED**&#xff08;已建立&…

在mac m1基于llama.cpp運行deepseek

lama.cpp是一個高效的機器學習推理庫&#xff0c;目標是在各種硬件上實現LLM推斷&#xff0c;保持最小設置和最先進性能。llama.cpp支持1.5位、2位、3位、4位、5位、6位和8位整數量化&#xff0c;通過ARM NEON、Accelerate和Metal支持Apple芯片&#xff0c;使得在MAC M1處理器上…

多模態大語言模型arxiv論文略讀(154)

Visual-Oriented Fine-Grained Knowledge Editing for MultiModal Large Language Models ?? 論文標題&#xff1a;Visual-Oriented Fine-Grained Knowledge Editing for MultiModal Large Language Models ?? 論文作者&#xff1a;Zhen Zeng, Leijiang Gu, Xun Yang, Zhan…

Python PDF處理庫深度對比:PyMuPDF、pypdfium2、pdfplumber、pdfminer的關系與區別

Python PDF處理庫深度對比&#xff1a;PyMuPDF、pypdfium2、pdfplumber、pdfminer的關系與區別前言1. 庫的基本介紹1.1 PyMuPDF (fitz)1.2 pypdfium21.3 pdfplumber1.4 pdfminer2. 關系圖譜3. 核心區別對比3.1 性能對比3.2 功能對比4. 代碼示例對比4.1 基本文本提取PyMuPDFpypd…