動態內存分配與智能指針

內存分配:

靜態存儲區:

  1. 局部static對象
  2. 類的static數據成員
  3. 定義在任何函數之外的變量

棧區:

  1. 函數內的非static對象

動態內存分配的方式有:

  1. new和delete
  2. 智能指針(shared_ptr、unique_ptr、weak_ptr)
  3. allocator類
  4. malloc和free

直接管理內存:

運算符new分配內存,delete釋放new分配的內存。

int* p = new int ();//new表達式在自由空間構造一個對象,并返回指向該對象的指針。
delete p;
int* pi = new int[10]();//分配一個10個int的數組,pi指向第一個int。

默認情況下,動態分配的對象是默認初始化的,這意味著內置類型或者組合類型的對象的值是未定義的,而類類型對象將用默認構造函數進行初始化
new分配const對象是合法的,但const對象必須進行初始化
如果一個程序用光了所有可用的內存,new表達式會失敗,并拋出bad_alloc異常,該異常可以通過nothrow阻止,阻止異常拋出的new稱為定位new

int* p1 = new int;//如果分配失敗,new拋出std::bad_alloc。
int* p2 = new (nothrow)int;//如果分配失敗,new返回一個空指針。

delete表達式也執行兩個動作:銷毀一個給定的指針指向的對象;釋放對應的內存。
傳遞給delete的指針必須指向動態分配的內存,或者是一個空指針,釋放一個并非new分配的內存,或者將相同的指針值釋放多次,其行為是未定義的
釋放動態數組時,數組中的元素按逆序銷毀
釋放一個const動態對象,只要delete指向它的指針即可。

const int* pci = new const int(1024);
delete pci;

直接管理內存容易犯的錯:

  1. 忘記delete內存(內存泄漏)
  2. 使用已經釋放掉的對象
  3. 同一塊內存釋放兩次

空懸指針/野指針(dangling pointer):指向一塊曾經保存數據對象但現在已經無效的內存的指針。
產生原因:指針變量聲明時未初始化或者指針被delete或free后未置為nullptr以及指針操作超越了變量的作用范圍

智能指針:

shared_ptr允許多個指針指向同一個對象
unique_ptr“獨占”所指向的對象
weak_ptr指向一個shared_ptr管理的對象,它不控制指向對象的生存期。

shared_ptr:
初始化:

shared_ptr<string> p1;//默認初始化的智能指針保存著一個空指針

智能指針的使用方式與普通指針類似。解引用一個智能指針返回它指向的對象,如果在一個條件判斷中使用智能指針,效果是檢測它是否為空。

if (p1 && p1->empty()) {*p1 = "hi";//如果p1指向一個空string,解引用p1,將一個新值賦予string。
}

make_shared函數:
類似順序容器的emplace成員,make_shared用其參數來構造給定類型的對象。

shared_ptr<int> pi = make_shared<int>(42);

引用計數:
每個shared_ptr都有一個關聯的計數器,通常稱其為引用計數。無論何時我們拷貝一個shared_ptr,或將其作為參數傳遞給一個函數以及作為一個函數的返回值時,它所關聯的計數器就會遞增。當我們給shared_ptr賦予一個新值或者是shared_ptr被銷毀時,計數器就會遞減。當一個shared_ptr的計數器變為0,他就會自動釋放自己所管理的對象。

程序使用動態內存的三個原因:

  1. 程序不知道使用多少個對象(容器類)
  2. 程序不知道所需對象的具體類型
  3. 程序需要在多個對象間共享數據

shared_ptr與new的結合:
接受指針參數的智能指針構造函數是explicit的,因此我們不能將一個內置指針隱式轉換為一個智能指針,必須使用直接初始化的形式

shared_ptr<int> p1 = new int(1024);//錯誤
shared_ptr<int> p1(new int(1024));//正確

如果想用shared_ptr管理動態數組,必須提供自己定義的刪除器,并且不支持下標操作。

shared_ptr<int> sp(new int[10],[](int *p){delete [] p ;});
sp.reset();

正確使用智能指針的規范:

  1. 使用相同的內置指針值初始化(或reset)多個智能指針。
  2. 不delete get() 返回的指針。
  3. 不使用get()初始化或reset另一個智能指針。
  4. 如果你使用get()返回的指針,記住當最后一個對應的智能指針銷毀后,你的指針就變為無效了。
  5. 如果你使用智能指針管理的資源不是new分配的內存,記住傳遞給它一個刪除器。

unique_ptr:
當我們定義unique_ptr時,需要將其綁定到一個new返回的指針上,而且必須采用直接初始化的形式。
由于unique_ptr“獨占”它指向的對象,因此unique_ptr不支持拷貝或賦值操作
雖然我們不能拷貝和賦值unique_ptr,但可以調用release或reset將指針所有權從一個(非const)unique_ptr轉移給另一個unique:

unique_ptr<string> p1(new string("first"));
unique_ptr<string> p2(p1.release());//將所有權從p1轉向p2,p1被置為空
unique_ptr<string> p3(new string ("second"));
//將所有權從p3轉移給p2.
p2.reset(p3.release());//reset釋放了p2原來指向的內存。

unique_ptr可以管理動態數組,必須在對象類型后面跟一對空方括號,支持下標操作。

unique_ptr<int[]> up(new int[10]);
up.release();//自動用delete[]銷毀其指針。
for(size_t i = 0 ; i != 10 ; i++)
{up[i] = i;
}

weak_ptr:
weak_ptr不控制所指向的對象的生存期,指向一個由shared_ptr管理的對象。將一個weak_ptr不會改變shared_ptr的引用計數。一旦最后一個指向對象的shared_ptr被銷毀,對象就會被釋放。
由于對象可能不存在,不能使用weak_ptr直接訪問對象,必須調用lock。此函數檢查weak_ptr指向的對象是否仍存在。如果存在,lock返回一個指向共享對象的shared_ptr。
可以解決兩個shared_ptr互相引用的問題。

allocator類:

將內存分配和對象構造分離開,提供一種類型感知的內存分配方法,他分配的內存是原始的、未構造的。
定義一個allocator對象,必須指明這個allocator可以分配的對象類型。當一個allocator對象分配內存時,它會根據給定的對象類型來確定恰當的內存大小和對齊位置:

allocator<string> alloc;//可以分配string的allocator對象
auto const p = alloc.allocate(n);//分配n個未初始化的string。
auto q = p;
alloc.construct(q++);//*q為空字符串
alloc.construct(q++,10,'c');//*q為cccccccccc
alloc.construct(q++,"hi");//*q為hi
cout<<*p<<endl;//正確:使用string的輸出運算符
cout<<*q<<endl;//災難:q指向未構造的內存
while(q!=p)alloc.destroy(--q);//釋放我們真正構造的string
//destory接受一個指針,對指向的對象執行析構函數。
alloc.deconstruct(p,n);//通過deallocate釋放內存。
//傳遞給deallocate的指針不能為空,它必須指向由allocate分配的內存。而且傳遞給deallocate的大小參數必須與調用allocated分配內存時提供的大小參數具有一樣的值。

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

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

相關文章

1151壓力變送器型號_日本進口橫河EJA530E壓力變送器型號解讀!

橫河EJA變送器對大家來說也許不陌生&#xff0c;但是對于EJA變送器的型號很多人還不是很懂&#xff0c;因為一個全型號代表這很多參數&#xff0c;每一個字母和每一個數字背后都是一個準確的參數&#xff0c;我們在選型的時候要提供必要的參數&#xff0c;更具參數選出合適的型…

plc控制可調節閥流程圖_PLC控制的水箱液位控制系統畢業論文

內容介紹原文檔由會員 莎士比亞 發布論文標準WORD格式排版40頁摘要在人們生活以及工業生產等諸多領域經常涉及到液位和流量的控制問題, 例如居民生活用水的供應, 飲料、食品加工, 溶液過濾, 化工生產等多種行業的生產加工過程, 通常需要使用蓄液池, 蓄液池中的液位需要維持合適…

idea繼承后重新方法快捷鍵_idea 查看類繼承關系的快捷鍵

類似eclipse ctrlt的快捷鍵,idea中是ctrlH…找到對應的類 查看類關系圖…1.在想要查看的類上按 Ctrl H -> Diagrams -> Show Diagrams -> Java Class Diagrams -> Show Implementations -> Ctrl A -> 右擊一下 -> Enter .…打開想要查看的接口或者類文件…

怎樣在數組末尾添加數據_如何利用C++實現可變長的數組?

應該執行什么功能&#xff1f;假設我們要實現一個將自動擴展的數組類&#xff0c;是否需要實現函數&#xff1f;讓我們從下面主要功能使用的功能開始&#xff0c;看看我們需要實現哪些功能。輸出結果&#xff1a;0 1 2 3 40 1 2 100 4您需要做什么才能實現上述功能&#xff1f;…

Linux學習:第六章-Linux服務和進程管理

一進程查看 1psaux查看當前系統所有運行的進程 -a顯示前臺所有進程 -u顯示用戶名 -x顯示后臺進程 user&#xff1a; 用戶名 pid&#xff1a;進程id。PID1init系統啟動的第一個進程 %CPUcpu占用百分比 %MEM內存占用百分比 VSZ虛擬內存占用量KB RSS固定內存占有量 tty登錄終端tty…

覆蓋索引與聯合索引_淺析MySQL的索引覆蓋和索引下推

寫在前面在MySQL數據庫中&#xff0c;索引對查詢的速度有著至關重要的影響&#xff0c;理解索引也是進行數據庫性能調優的起點&#xff0c;索引就是為了提高數據查詢的效率。今天我們來聊聊在MySQL索引優化中兩種常見的方式&#xff0c;索引覆蓋和索引下推索引覆蓋要了解索引覆…

python3 輸入輸出_Python3基礎之輸入和輸出實例分析

通常來說&#xff0c;一個Python程序可以從鍵盤讀取輸入&#xff0c;也可以從文件讀取輸入&#xff1b;而程序的結果可以輸出到屏幕上&#xff0c;也可以保存到文件中便于以后使用。本文就來介紹Python中最基本的I/O函數。一、控制臺I/O1.讀取鍵盤輸入內置函數input([prompt])&…

redis 哨兵 異步_突破Java面試(23-8) - Redis哨兵主備切換的數據丟失問題-阿里云開發者社區...

1 數據丟失的兩個場景主備切換的過程&#xff0c;可能會導致數據丟失1.1 異步復制由于 master > slave的復制是異步的&#xff0c;所以可能有部分數據還沒復制到slave&#xff0c;master就宕機&#xff0c;于是這些數據就丟失了1.2 腦裂導致腦裂&#xff0c;也就是說&#x…

vue 斷開正在發送的請求_vue 發送請求頻繁時取消上一次請求

前言&#xff1a;在項目中經常有一些場景會連續發送多個請求&#xff0c;而異步會導致最后得到展示的結果可能不是最后一次發送請求返回的結果&#xff0c;且對性能也有非常大的影響。場景&#xff1a;列表式切換商品&#xff0c;有時候上一次請求的結果非常慢&#xff0c;而我…

Linux學習:第六章-Linux網絡配置

上一章的補充&#xff1a; 常見網絡端口&#xff1a; 2021ftp服務文件共享 22ssh服務安全遠程網絡管理 23telnet服務 25smtp&#xff1a;簡單郵件傳輸協議發信 110pop3&#xff1a;郵局協議收信 80www網頁服務 3306mysql端口 3389windows終端服務 53DNS端口 /etc/services所有系…

sequelize 增加數據庫字段_Node項目使用Sequelize操作數據庫(一)(包括模型,增,刪、改等)...

Sequelize 是一個基于 Promise 的 Node.js ORM&#xff0c;目前支持 Postgres、MySQL、SQLite 和 Microsoft SQL Server。它具有強大的事務支持&#xff0c;關聯關系、讀取和復制等功能。所謂ORM是指對象關系映射&#xff0c;通過使用描述對象和數據庫之間映射的元數據&#xf…

django orm插入一條_如何通過django的ORM遠程發布文章?

利用django的ORM可以方便的給數據庫插入文章但是假如我django放在阿里云&#xff0c;那我想在本地寫個插件&#xff0c;每天很方便的插入一些數據&#xff0c;最好是通過ORM的&#xff0c;因為管理起來比較方便&#xff0c;會涉及到多個站&#xff0c;可能會有200多個站&#x…

循環斐波那契數列_第五課:斐波那契數列(第一課時)

簡介&#xff1a;又稱黃金分割數列、因數學家列昂納多斐波那契以兔子繁殖為例子而引入&#xff0c;故又稱為“兔子數列”&#xff0c;指的是這樣一個數列&#xff1a;1、1、2、3、5、8、13、21、34……在數學上&#xff0c;斐波那契數列以如下被以遞推的方法定義&#xff1a;F(…

命令行 藍牙_Ubuntu使用BlueZ驅動藍牙dongle

藍牙dongle即藍牙適配器&#xff0c;一般為USB接口&#xff0c;通過USB連接到PC等設備。連接dongle后&#xff0c;PC即可使用驅動程序控制dongle連接其它藍牙設備。本文主要介紹在Ubuntu系統中安裝BlueZ的方法及藍牙的使用。01獲取BlueZBlueZ是Linux系統的官方藍牙協議棧&#…

Java在Windows下導出xml文件到Linux服務器上

最近由于公司項目需要&#xff0c;學習了在Windows平臺導出xml文件到Linux服務器上的指定目錄下的方法&#xff0c;&#xff08;注&#xff1a;這里的我的Linux是在本機上裝的虛擬機&#xff09;現在寫下來記錄一下&#xff01; 1.首先是項目截圖&#xff1a; 2.主要是類&…

java peek函數_Java 8 Stream Api 中的 peek 操作

1. 前言我在 Java 8 Stream API中的 map 和flatMap 中講述了Java8 Stream API中 map 操作和 flatMap 操作的區別。然后有小伙伴告訴我 peek 操作 也能實現元素的處理。但是你知道 map 和 peek 的區別嗎&#xff1f; map 我們在開頭文章已經講過了&#xff0c;你可以去詳細了解一…

iphone如何查看dns延遲_iPhone手機網速慢?1分鐘教你設置DNS,網速立馬翻一番

很多小伙伴在購機時選擇蘋果手機都是因為iOS系統的流暢度和精簡性&#xff0c;但iPhone在可玩性和信號方面是不如安卓手機的。大家在使用過程中一定遇到過這樣的情況&#xff0c;連接同一個無線網&#xff0c;但是蘋果的網速總是要比安卓慢&#xff0c;這該怎么辦呢&#xff1f…

mysql 關系_MySQL之關系

關系多對多的關系&#xff0c;如何通過mysql來表示站在老師的角度一個老師可以教多個學生&#xff0c;一個老師也可以教一個學生。站在學生的角度一個學生可以被一個老師教一個學生也可以被多個老師教結論&#xff1a;如果站在兩邊看都是一對多的情況&#xff0c;那么這個關系就…

mysql賬號密碼忘_mysql用戶名密碼忘記了解決方法

今天想用一下實驗室服務器的mysql&#xff0c;發現不記得用戶名密碼了。解決方法如下&#xff1a;1. 保證服務器處于安全的狀態&#xff0c;如果可以請拔掉網線...(不過我跳過了這一步&#xff0c;額)2. 修改/etc/my.cnf文件在[mysqld]的段中加入&#xff1a;skip-grant-tables…

copying mysql status_mysql慢查詢copying to tmp table

windows server&#xff0c;無論修改my.ini的tmp_table_size&#xff0c;max_heap_table_size到多少&#xff0c;情況都一樣。同樣的表和查詢語句&#xff0c;在本地運行&#xff0c;沒出現慢查詢。SELECTg.goods_id,g.goods_name,g.shop_price,g.goods_thumb,SUM(og.goods_num…