《C++ Primer》第12章 動態內存(二)

參考資料:

  • 《C++ Primer》第5版
  • 《C++ Primer 習題集》第5版

12.1.5 unique_ptr(P417)

unique “擁有”它所指向的對象,某個時刻只能有一個 unique_ptr 指向一個給定對象。

381a5b5e846786f8c43170926fca976

當我們定義一個 unique_ptr 時,需要將其綁定到一個 new 返回的指針上,且必須采用直接初始化的形式:

unique_ptr<double> p1;    // 空unique_ptr
unique_ptr<int> p2(new int());    // 指向一個值初始化的int

unique_ptr 不支持普通的拷貝和賦值操作:

unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(p1);    // 錯誤
unique_ptr<string> p3;
p3 = p2;    // 錯誤

我們可以通過 releasereset 將指針的所有權從一個(非 constunique_ptr 轉移給另一個 unique_ptr

unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(p1.release());
unique_ptr<string> p3;
p3.reset(p2.release());

傳遞unique_ptr參數和返回unique_ptr

不能拷貝 unique_ptr 的規則有一個例外:我們可以拷貝或賦值一個將要被摧毀的 unique_ptr

unique_ptr<int> clone(int p) {unique_ptr<int> ret(new int(p));return ret;
}    // 正確

unique_ptr傳遞刪除器

unique 默認使用 delete 釋放指向的對象。與 shared_ptr 不同的是,我們需要在構造 unique_ptr 時提供刪除器的類型:

unique_ptr<objT, delT> p(new objT, fcn);

以上一篇筆記中提到的網絡連接類為例:

void f(destination &d /* 其他參數 */) {connection c = connect(&d);unique_ptr<connection, decltype(end_connection)*>p(&c, end_connection);// 當p被銷毀時,調用end_connection
}

12.1.6 weak_ptr(P420)

weak_ptr 是一種不控制所指向對象生存期的智能指針,指向由一個 shared_ptr 管理的對象。將 weak_ptr 綁定到一個 shared_ptr 不會改變 shared_ptr 的引用計數,一旦最后一個指向對象的 shared_ptr 被銷毀,即使有 weak_ptr 指向對象,對象也還是會被釋放。

a99c3cc2f6bec742c0f663d36af300e

當我們創建一個 weak_ptr 時,要用 shared_ptr 來初始化它:

auto p = make_shared<int>();
weak_ptr<int> wp(p);

上述代碼中 wp 不會改變 p 的引用計數。由于 wp 指向的對象可能被釋放掉,我們不能使用 weak_ptr 直接訪問對象,而必須調用 lock

if(shared_ptr<int> np = wp.lock()){    // 如果np不為空則條件成立...
}

核查指針類

為了展示 weak_ptr 的用途,我們為 StrBlob 類定義一個伴隨指針類 StrBlobPtr ,類中保存一個 weak_ptr ,指向 StrBlobdata 成員。使用 weak_ptr 可以阻止用戶訪問一個不再存在的 vector

class StrBlobPtr {
public:StrBlobPtr(): curr(0) {}StrBlobPtr(StrBlob &a, size_t sz = 0) :wptr(a.data), curr(sz){}string &deref() const;StrBlobPtr &incr();
private:shared_ptr<vector<string>> check(size_t, const string &) const;weak_ptr<vector<string>> wptr;size_t curr;    // 在數組中的當前位置
};

StrBlobPtrcheck 成員和 StrBlob 中的同名成員不同,它還要額外檢查指向的 vector 是否存在:

shared_ptr<vector<string>>
StrBlobPtr::check(size_t i, const string &msg)const {auto ret = wptr.lock();if (!ret)throw runtime_error("unbound StrBlobPtr");if (i >= ret->size())throw out_of_range(msg);return ret;
}

指針操作

我們定義 derefincr 用來解引用和遞增 StrBlobPtr

string &StrBlobPtr::deref()const {auto p = check(curr, "dereference past end");return (*p)[curr];
}StrBlobPtr &StrBlobPtr::incr() {check(curr, "increment past end of StrBlobPtr");++curr;return *this;
}

由于我們在初始化 StrBlobPtr 時需要用到 StrBlob 中的 data 成員,所以我們要將 StrBlobPtr 聲明成 StrBlob 的友元。

12.2 動態數組(P423)

C++ 和標準庫提供了兩種一次分配一個對象數組的方法。在大多數情況下,我們應該使用容器而非動態數組,使用容器的類可以使用默認版本的拷貝、賦值、析構操作,而使用動態數組的類必須定義自己版本的操作。

new和數組(P423)

為了讓 new 分配一個對象數組,我們要在類型名后跟一對方括號,在其中指明要分配的對象的數目:

int *pia = new int[get_size()];    // 方括號中必須為整型,但不必為常量

也可以用類型別名來分配數組:

using arrT = int[1024];
int *p = new arrT;

分配一個數組會得到一個元素類型的指針

無論用 new T[] 還是類型別名,我們得到的都是一個指向數組元素類型的指針,而不是一個數組。下面的代碼驗證了這個事實:

int x = 0;
decltype(new int[10]) p1 = &x;    // 正確
int arr[10];
decltype(arr) p2 = &x;    // 錯誤

動態數組并不是數組類型

初始化動態分配對象的數組

默認情況下,new 分配的對象,不論是單個對象還是數組,都是默認初始化的。要對數組中的元素執行值初始化,可以在大小后跟一對圓括號:

int *pia1 = new int[10];    // 10個默認初始化的int
int *pia2 = new int[10]();    // 10個值初始化的int

在新標準中,我們還可以提供初始值列表:

int *pia3 = new int[10] {0, 1, 2, 3};

動態分配一個空數組是合法的

char arr[0];    // 錯誤
char *cp = new char[0];    // 正確

當我們用 new 分配一個大小為 0 的數組時,new 返回一個合法的非空指針。

釋放動態數組

為了釋放動態數組,我們也要在 delete 后跟一對方括號:

delete p;    // p必須指向一個動態分配的對象或為空
delete [] pa;    // pa必須指向一個動態分配的對象數組或為空

數組中的元素按逆序銷毀。如果我們在 delete 一個數組時忽略了方括號或在 delete 一個對象時使用了方括號,結果是未定義的。

前面提到,當我們使用類型別名來定義數組類型時,在 new可以不使用方括號,但是在 delete 時則必須使用方括號:

using arrT = int[1024];
auto p = new arrT;
delete[] p;

此處產生一個疑問,既然前面提到,new[] 得到的僅僅是一個指針,而并不是一個數組,那么 delete[] 是怎么知道需要釋放多少空間的呢?答案見C++中delete是如何獲知需要釋放的內存(數組)大小的? - 知乎 (zhihu.com)

智能指針和動態數組

標準庫提供了一個可以管理 new 分配的數組的 unique_ptr 版本:

unique_ptr<int[]> up(new in[10]);
up.release();    // 自動使用delete[] 
4540dd42fbdbdd927fbe0ab578c2939

unique_str 指向一個數組時,我們不能使用點運算符箭頭運算符,但我們可以使用下標運算符訪問數組中的元素。

shared_ptr 不支持直接管理動態數組。如果希望使用 shared_ptr 管理動態數組,必須定義自己的刪除器:

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

如果未提供刪除器,shared_ptr 將使用 delete 釋放一個動態數組,這個行為是未定義的。由于 shared_ptr 不支持下標運算符,為了訪問訪問數組中的元素,必須用 get 獲得一個內置指針:

for (size_t i = 0; i != 10; ++i) {*(sp.get() + i) = i;
}

12.2.2 allocator類(P427)

new 在靈活性上有一些局限,因為它將內存分配和對象構造組合在一起了。當分配一大塊內存時,我們通常希望將內存分配和對象構造分離,而將內存分配和對象構造組合在一起可能造成不必要的浪費:

// 初始化了n個string,但某些string可能永遠用不到
string *const p = new string[n];

此外,沒有默認構造函數的類不能用 new 分配動態數組。

allocator

allocator 類定義在頭文件 memory 中,它幫助我們將內存分配和對象構造分離開來。

7503fa1c99d8e1eb3b70fa1f0002f50
allocator<string> alloc;
const auto p = alloc.allocate(n);     // 分配n個未初始化的string

allocator分配未構造的內存

auto q = p;    // 頂層const被忽略
alloc.construct(q++);
alloc.construct(q++, 10, 'c');
alloc.construct(q++, "hi");

當我們用完對象后,必須對每個元素調用 destroy 銷毀它們:

while(q != p){alloc.destroy(--q);    // 釋放真正構造的string
}

調用 deallocate 釋放內存:

alloc.deallocate(p, n);

拷貝和填充未初始化內存的算法

標準庫還為 allocator 類定義了兩個伴隨算法,定義在頭文件 memory 中:

d40ebd3f94a185266f1296424db3535
allocator<string> alloc;
vector<string> vs = {"hello", "hi", "him"};
auto p = alloc.allocate(vs.size() * 2);
auto q = uninitialized_copy(vs.begin(), vs.end(), p);
uninitialized_fill_n(q, vs.size(), "world");
d40ebd3f94a185266f1296424db3535
allocator<string> alloc;
vector<string> vs = {"hello", "hi", "him"};
auto p = alloc.allocate(vs.size() * 2);
auto q = uninitialized_copy(vs.begin(), vs.end(), p);
uninitialized_fill_n(q, vs.size(), "world");

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

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

相關文章

游戲中小地圖的制作__unity基礎開發教程

小地圖的制作 Icon標識制作制作攝像機映射創建地圖UI效果“不一樣的效果” 在游戲中經常可以看到地圖視角的存在&#xff0c;那么地圖視角是如何讓實現的呢&#xff1f; 這一期教大家制作一個簡易的小地圖。 &#x1f496;點關注&#xff0c;不迷路。 老樣子&#xff0c;我們還…

Leetcode 131 分割回文串

題意理解&#xff1a; 分割回文子串&#xff0c;可以看作是劃分連續的字幕組合——所以也可以用回溯的方法來解決 每個位置選與不選——該位置切割|不切割 對于每一段子串——>判斷是否是回文串&#xff1a; 是&#xff1a; 繼續切割 不是&#xff1a; 剪枝 解題方法…

Ubuntu Destktop 22.04 設置 ssh 超時時間

Ubuntu Destktop 22.04 使用 ssh 連接服務器時&#xff0c;發現一段時間不操作就會自動斷開連接&#xff0c;解決方法如下&#xff1a; 打開 /etc/ssh/ssh_config 文件&#xff1a; sudo vim /etc/ssh/ssh_config在文件最后添加&#xff1a; # ssh 客戶端會每隔 30 秒發送一…

在線免費制作各種證件照,有需要的收藏

現在很多場合都需要一寸證件照&#xff0c;比如辦理身份證、出國簽證等。以往&#xff0c;我們都需要到專門的照相館拍攝&#xff0c;但是現在&#xff0c;有了隨時照微信小程序&#xff08;抖音和支付搜索億鳴證件照哦&#xff09;&#xff0c;你可以足不出戶就能夠制作一寸證…

linux shell

文章目錄 預設參數腳本自動開終端if語句語法常用判斷命令文件/目錄判斷&#xff1a;字符串判斷數值判斷邏輯判斷 if高級特性&#xff1a; 預設參數 $$ Shell本身的PID&#xff08;ProcessID&#xff09;$! Shell最后運行的后臺Process的PID$? 最后運行的命令的結束代碼&#…

MySQL InnoDB Replication部署方案與實踐

1. 概述 MySQL Innodb ReplicaSet 是 MySQL 團隊在 2020 年推出的一款產品&#xff0c;用來幫助用戶快速部署和管理主從復制&#xff0c;在數據庫層仍然使用的是主從復制技術。 ReplicaSet 主要包含三個組件&#xff1a;MySQL Router、MySQL Server 以及 MySQL Shell 高級客戶…

eventBus父組件$emit一次子組件多次收到¥

eventBus父組件$emit一次子組件多次收到$on 參考&#xff08;EventBus踩坑1-CSDN博客&#xff09; 父組件emit出了事件&#xff0c;這個過程需要一定時間&#xff0c;這段時間過長&#xff0c;子組件還未接收到父組件的emit&#xff0c;父組件認為子組件沒有收到&#xff0c;…

12 位多通道國產芯片ACM32F403/F433 系列,支持 MPU 存儲保護功能,應用于工業控制,智能家居等產品中

ACM32F403/F433 芯片的內核基于 ARMv8-M 架構&#xff0c;支持 Cortex-M33 和 Cortex-M4F 指令集。芯片內核 支持一整套DSP指令用于數字信號處理&#xff0c;支持單精度FPU處理浮點數據&#xff0c;同時還支持Memory Protection Unit &#xff08;MPU&#xff09;用于提升應用的…

Java - Mybatis借助PageHelper實現分頁,集成SpringBoot

未使用SpringBoot 第?步&#xff1a;引?依賴 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.3.1</version> </dependency> 第?步&#xff1a;在mybatis-config.xml…

PyTorch張量:內存布局

你可能對 torch 上的某些函數感到困惑&#xff0c;它們執行相同的操作但名稱不同。 例如&#xff1a; reshape()、view()、permute()、transpose() 等。 這些函數的做法真的不同嗎&#xff1f; 不&#xff01; 但為了理解它&#xff0c;我們首先需要了解一下張量在 pytorch 中…

1 CPU實現的基本框圖

匯編語言 && 指令格式 CPU設計的框架&#xff1a;三級流水線 ROM存放指令和數據&#xff0c;大端模式&小端模式&#xff0c;地址對齊 取指 譯碼&#xff1a; 執行&#xff1a; 匯編語言 & 指令格式 流水線實現工作機制 模塊功能劃分&接口信號 參考…

Linux中用rpm管理軟件

本章主要介紹使用rpm對軟件包進行管理 使用rpm查詢軟件的信息使用rpm安裝及卸載軟件使用rpm對軟件進行更新使用rpm對軟件進行驗證 rpm 全稱是redhat package manager&#xff0c;后來改成rpm package manager&#xff0c;這是根據源 碼包編譯出來的包。先從光盤中拷貝一個包&…

strict-origin-when-cross-origin

嚴格限制同源策略 &#xff08;1&#xff09;允許服務器的同源IP地址訪問 &#xff08;2&#xff09;允許Referer --- 后端服務器要配置

linux sed命令刪除一行/多行_sed刪除第一行/linux刪除文件某一行

sed系列文章 linux常用命令(9)&#xff1a;sed命令(編輯/替換/刪除文本)linux sed命令刪除一行/多行_sed刪除第一行/linux刪除文件某一行 文章目錄 sed系列文章一、sed刪除某一行內容/刪除最后一行二、sed刪除多行三、擴展3.1、-i命令 本文主要講解如何刪除txt文件中的某一行內…

vite+ts——user.ts——ts接口定義+axios請求的寫法

import axios from axios; import qs from query-string; import {UserState} from /store/modules/user/types;export interface LoginData{username:string;password:string;grant_type?:string;scope?:string;client_id?:string;client_secret?:string;response_type?:…

企業使用APP自動化測試工具的重要因素

隨著移動應用市場的蓬勃發展&#xff0c;企業對高質量、高效率的軟件交付提出了更高的要求。在這個背景下&#xff0c;APP自動化測試工具成為了企業不可或缺的一部分。以下是企業采用APP自動化測試工具的關鍵因素&#xff1a; 1. 快速且可重復的測試執行 自動化測試工具能夠快速…

Docker入門概念

文章目錄 容器&#xff08;container&#xff1a;容器/集裝箱&#xff09;技術虛擬機解決了哪些部署問題docker解決了哪些部署問題docker是如何做到容器間運行時環境隔離的docker基本概念docker基本使用 容器&#xff08;container&#xff1a;容器/集裝箱&#xff09;技術 容…

奧威亞視頻云平臺VideoCover.aspx 接口任意文件上傳漏洞復現 [附POC]

文章目錄 奧威亞視頻云平臺VideoCover.aspx 接口任意文件上傳漏洞復現 [附POC]0x01 前言0x02 漏洞描述0x03 影響版本0x04 漏洞環境0x05 漏洞復現1.訪問漏洞環境2.構造POC3.復現0x06 修復建議奧威亞視頻云平臺VideoCover.aspx 接口任意文件上傳漏洞復現 [附POC] 0x01 前言 免責…

做數據分析為何要學統計學(5)——什么問題適合使用卡方檢驗?

卡方檢驗作為一種非常著名的非參數檢驗方法&#xff08;不受總體分布因素的限制&#xff09;&#xff0c;在工程試驗、臨床試驗、社會調查等領域被廣泛應用。但是也正是因為使用的便捷性&#xff0c;造成時常被誤用。本文參閱相關的文獻&#xff0c;對卡方檢驗的適用性進行粗淺…

原來使用代碼也可以畫時序圖,用這個Mermaid就行,真香

本文首發于我的個人掘金博客&#xff0c;看到很多人都比較喜歡這篇文章&#xff0c;分享給大家。 個人博客主頁&#xff1a;https://www.aijavapro.cn 個人掘金主頁&#xff1a;juejin.cn/user/2359988032644541/posts 個人知識星球: 覺醒的新世界程序員 一、背景 在軟件開發和…