淺談new與::operator new

目錄

前言

1.為什么C++要引入new/delete?

2.operator new與operator delete函數

它們的實際作用

Placement New(定位new表達式)

總結



前言

在寫上一篇博客“vector的模擬實現”時,我一直很好奇vector的private成員為什么要用三個封裝后的迭代器指針,來替代原本數據結構順序表中的成員變量,因為原本的數據結構成員是完全滿足需求的,即:

(從數據結構的本質來講,vector完全可以被理解為 C++ 版的、功能更強大的、高度自動化的順序表。)

在學習了實際庫中的vector代碼后,我發現了未見過的東西:"::operator new與::operator delete"。在知道了它們的作用與使用場所后,或許我找到了用迭代器指針替代原本數據成員的原因。


1.為什么C++要引入new/delete?

C語言內存管理方式在C++中是可以繼續使用,比如malloc、realloc等函數。但有些地方這些傳統的空間申請函數就顯得有些無能為力,而且使用起來比較麻煩。

是的,在C++的自定類型數據中,常需要在定義時順便調用構造函數初始化。而原C語言的空間申請函數是沒有這種功能,需要額外操作,于是C++提出了自己的內存管理方式:通過new和delete操作符進行動態內存管理。

簡而言之,什么C++要引入new/delete的原因在于:

new/delete 和 malloc/free最大區別是 new/delete對于【自定義類型】除了開空間時,還會調用構造函數和析構函數。


2.operator new與operator delete函數

首先介紹它們是什么:

它們是C++中的內存分配原語函數它們只負責分配和釋放原始內存,不涉及對象的構造和析構。

它們的用法:

#include <new> // 包含 operator new 和 operator delete 的聲明// 分配內存
void* memory = ::operator new(size_t bytes);// 釋放內存
::operator delete(void* ptr);

示例

#include <iostream>
#include <new>int main() 
{// 分配10個int大小的內存void* int_memory = ::operator new(10 * sizeof(int));std::cout << "內存分配成功,地址: " << int_memory << std::endl;// 使用內存...// 釋放內存::operator delete(int_memory );std::cout << "內存已釋放" << std::endl;return 0;
}

它們與new/delete的關系:

new和delete是用戶進行動態內存申請和釋放的操作符,operator new 和operator delete是系統提供的全局函數,new在底層調用operator new全局函數來申請空間,delete在底層通過 operator delete全局函數來釋放空間。

操作做的事情
int* p = new int(42);1. 調用?operator new(sizeof(int))?分配內存
2. 在內存上調用?int?的構造函數?構造對象(設為42)
delete p;1. 調用?p?的析構函數?析構對象
2. 調用?operator delete(p)?釋放內存
void* mem = ::operator new(sizeof(int));只分配內存,不調用構造函數
::operator delete(mem);只釋放內存,不調用析構函數

簡單說:new/delete?=? (operator new/operator delete?+ 構造函數/析構函數調用)

注意:和new與delete,new[ ]與delete[ ]相同,operator new只能與operator delete匹配。

它們的實際作用

回到前言部分,為什么vector的private成員為什么要用三個封裝后的迭代器指針,來替代原本數據結構順序表中的成員變量呢?

在查看vecotr的實際實現代碼中,我發現 vector類中涉及空間增刪的函數,實際上都是調用的reserve函數,而在reserve函數中則使用了operator new/delete。

我們來看看reserve函數的模擬實現:

		void reserve(size_t n){size_t oldSize = size(), oldCapa = capacity();if (n > oldCapa){size_t newcapa = oldCapa == 0 ? 16 : oldCapa * 2;while (newcapa < n)newcapa *= 2;iterator newVec =(iterator)::operator new(newcapa * sizeof(T));if (_start){for (int i = 0; i < oldSize; ++i)new(newVec + i)T(_start[i]);for (int i = 0; i < oldSize; ++i)_start[i].~T();}::operator delete(_start);_start = newVec;_finish = _start + oldSize;_end = _start + newcapa;}else return;}

討論:在普通的reserve函數中,我們通常用new來申請空間,如下所示:

T* newcapa=new T[n];

可這帶來一個問題:new在申請空間的同時會調用構造函數,可是這些空間我們真的需要全部初始化嗎,換句話說這些空間我們真能全部用完嗎?顯然大部分場景是用不完的,因為vector的空間一般呈*2倍速度增長。那么這些不用的空間通過調用構造函數初始化,這不僅造成了一定的性能浪費,還讓后續無法自定義使用這片內存,造成內存資源浪費。

于是,通過用operator new/delete替換之前的new/delete,既解決了性能損失,又滿足了C++內存分配與對象構造分離的目的。

或許有讀者注意到上述代碼中的如下這段代碼,這段代碼實則揭開了為什么vector要使用三個指針作為成員變量的原因:

for (int i = 0; i < oldSize; ++i)new(newVec + i)T(_start[i]);

已知C++程序的一個核心設計思想:將內存分配(Allocation)和對象構造(Construction)分離。通過使用operator new/delete確實做到了只分配空間,不調用構造函數的目的。那么什么時候調用構造函數呢?

——在reserve函數中,通過提前記錄的oldSize精確控制調用構造函數的次數。而實際調用構造函數是通過Placement New(定位new表達式)實現的。

Placement New(定位new表達式)

它是什么?

Placement new 是一種特殊的 new 表達式,它在已分配的內存上構造對象。它不分配內存,只調用構造函數。

語法

new (address) Type(constructor_arguments);

address:一般傳入指針;

Type:某數據類型,可以是內置類型,也可以是自定義類型;constructor_arguments,該類型的構造函數。

使用示例

#include <iostream>
#include <new>class MyClass {
public:int value;MyClass(int v) : value(v) {std::cout << "構造函數被調用,value = " << value << std::endl;}~MyClass() {std::cout << "析構函數被調用,value = " << value << std::endl;}
};int main() {// 1. 只分配內存,不構造對象void* memory = ::operator new(sizeof(MyClass));// 2. 在已分配的內存上構造對象MyClass* obj = new (memory) MyClass(42);std::cout << "對象值: " << obj->value << std::endl;// 3. 顯式調用析構函數obj->~MyClass();// 4. 釋放內存::operator delete(memory);return 0;
}

operator new?設計時就考慮了與 placement new 的配合使用。這種組合提供了對對象構造和內存分配的完全控制。

現在回到上述有關reserve函數的討論。

現在已知vector中有關的空間操作,全是由reserve函數完成的,其中reserve函數通過使用operator new/delete、定位new表達式完成了“內存分配和對象構造的分離”。

結合定位new表達式的使用語法,有關“vector的private成員為什么要用三個封裝后的迭代器指針,來替代原本數據結構順序表中的成員變量”的答案也就呼之欲出了,或許原因之一就是為了滿足位new表達式的使用語法從而達到“內存分配和對象構造的分離”的目的。

同樣是申請空間,為什么不使用原C語言malloc等函數,而設計出operator new函數呢?

可能的原因或許有很多,但作者認為或許與它們在面對異常時的反應不同:

operator new?的異常行為

  • 當?operator new?無法分配內存時,它會拋出?std::bad_alloc?異常,提醒程序員。

  • 這與 C++ 的異常處理機制完美集成。

malloc?的錯誤處理

  • 當?malloc?無法分配內存時,它返回?NULL(或 C++11 中的?nullptr),需要程序員自己檢查。

  • 這要求你檢查返回值,使用 C 風格的錯誤處理。


總結

本文從對“vector的private成員為什么要用三個封裝后的迭代器指針,來替代原本數據結構順序表中的成員變量”疑問中,引出operator new/delete的介紹,以及之后定位表達式new的使用語法。

本文或許對vector為什么要用三個指針,替換原本的使用一個指針加兩個size_t(data, size, capacity)的回答不盡完美,甚至漏洞百出,但好在因此學到了新東西。

感謝你的閱讀。

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

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

相關文章

Java中Integer轉String

在 Java 中&#xff0c;將 Integer 轉換為 String 有多種方法&#xff0c;以下是常見的幾種方式&#xff1a;1. 使用 Integer.toString() 方法javaInteger num 123; String str Integer.toString(num); // 直接調用 Integer 的靜態方法2. 使用 String.valueOf()javaInteger n…

智能裝備如何與軟件結合?

一、什么是智能裝備&#xff1f; 智能裝備是具備“感知-決策-執行-自適應”閉環能力的智能化系統&#xff0c;本質是“傳統物理裝備”與“數字智能”的深度融合。它不僅能完成預設動作&#xff08;如傳統機械臂焊接&#xff09;&#xff0c;還能通過傳感器“觀察”環境、用算法…

react性能優化有哪些

React 性能優化的手段比較多&#xff0c;既有代碼層面的&#xff0c;也有構建層面的&#xff0c;還涉及到運行時調優。我幫你系統性梳理一份&#xff1a;&#x1f539; 一、渲染性能優化1. 減少不必要的渲染React.memo&#xff1a;對函數組件做淺比較&#xff0c;避免相同 prop…

騰訊云OpenCloudOS 9系統部署OpenTenBase數據庫詳細教程

OpenTenBase簡介OpenTenBase是一個關系型數據庫集群平臺&#xff0c;提供寫入可靠性和多節點數據同步功能。可以在一臺或多臺主機上配置OpenTenBase&#xff0c;并將數據存儲在多個物理主機上。OpenTenBase架構組件&#xff1a;Coordinator Node (CN)&#xff1a;應用程序訪問入…

【計算機視覺】Pixel逐像素分類Mask掩碼分類理解摘要

目標檢測和實例分割是計算機視覺的基本任務。目標檢測的傳統方法中通常利用邊界框技術進行對象定位&#xff0c;然后利用逐像素分類為這些本地化實例分配類。但是當處理同一類的重疊對象時&#xff0c;或者在每個圖像的對象數量不同的情況下&#xff0c;這些方法通常會出現問題…

C++之stack類的代碼及其邏輯詳解

1. stack介紹及使用方法stack是一種后進先出的數據結構&#xff0c;所以在C的STL庫中也同樣遵循了這一點&#xff0c;我們在使用的時候不支持隨機訪問或迭代器遍歷。注意事項調用 top() 或 pop() 前需確保棧非空&#xff0c;否則可能引發未定義行為。stack 沒有 clear() 函數&a…

Spring Cache實現簡化緩存功能開發

一. 介紹Spring Cache 是 Spring 框架提供的緩存抽象層&#xff0c;它簡化了在應用中添加緩存功能的開發工作。通過 Spring Cache&#xff0c;開發者無需關注具體緩存實現的細節&#xff0c;只需通過注解就能快速實現方法級別的緩存管理。核心特點1. 與具體緩存實現解耦&#x…

Lombok(簡化Java當中的開發)

Lombok概述 以前的Java項目中,充斥著太多不友好的代碼:POJO的getter/setter/toString/構造方法;打印日志;I/O流的關閉操作等等,這些代碼既沒有技術含量,又影響著代碼的美觀,Lombok應運而生。 LomBok可以通過注解,幫助開發人員消除JAVA中尤其是POJO類中的冗長代碼。 使…

【DeepSeek】公司內網部署離線deepseek+docker+ragflow本地模型實戰

企業內部可能有些數據比較敏感&#xff0c;不能連接互聯網。本次實驗操作是將deepseek完全離線后遷移至內網使用&#xff0c;實驗基于Windows server 2022 datacenter系統安裝deepseek、docker、ragflow。 目錄使用VMware新建WIN2022虛擬機一、安裝DeepSeek模型二.安裝Docker使…

【軟考架構】面向服務的體系結構(SOA)深度解析

面向服務的體系結構&#xff08;SOA&#xff09;深度解析 面向服務的體系結構&#xff08;Service-Oriented Architecture, SOA&#xff09;是一種以服務為核心的軟件架構范式&#xff0c;通過標準化接口實現異構系統間的高效集成與協作。以下從概念定義、發展脈絡、技術演進、…

centos7中MySQL 5.7.32 到 5.7.44 升級指南:基于官方二進制包的原地替換式升級

目錄前言1. 升級概述1.1 升級背景1.2 升級目的1.3 升級方法概述1.4 升級策略與注意事項2. 升級準備2.1 備份工作2.2 下載目標版本2.3 停止 MySQL 服務3. 替換二進制文件3.1 解壓官方二進制包3.2 替換核心二進制文件3.3 更新共享庫4. 執行升級并驗證4.1 啟動 MySQL 服務4.2 監控…

數學七夕花禮(MATLAB版)

前言參考的視頻在抖音&#xff0c;電腦版的抖音一直登錄不了&#xff0c;用手機分享的鏈接如下所示。4.35 Iv.FH yTl:/ 04/04 復制打開抖音&#x1f440;數學送的七夕花禮&#xff0c;記得查收噢.# 七夕花禮請查收 ... https://v.douyin.com/H-YpOJCyQyg/rho4sin(8theta)公式&a…

LeetCode - 21. 合并兩個有序鏈表

題目 21. 合并兩個有序鏈表 思路 我會采用雙指針的方法&#xff0c;同時遍歷兩個鏈表&#xff0c;比較當前節點的值&#xff0c;將較小的節點添加到結果鏈表中。 具體思路是這樣的&#xff1a; 首先創建一個啞節點(dummy node)作為合并后鏈表的頭部&#xff0c;這樣可以簡…

ES01-環境安裝

ES01-環境安裝 文章目錄ES01-環境安裝1-參考網址2-知識總結1-參考網址 elasticsearch官網地址&#xff1a;https://www.elastic.co/安裝elasticsearch9.0.0參考&#xff1a;https://zhuanlan.zhihu.com/p/1920780524991017021安裝elasticsearch9.0.0參考&#xff1a;http://ww…

UI前端大數據可視化實戰策略:如何設計符合用戶認知的數據可視化界面?

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩!UI前端大數據可視化實戰策略&#xff1a;如何設計符合用戶認知的數據可視化界面&#xff1f;數…

學習python第15天

其實前面學的根本不記得了&#xff0c;小丑.jpg&#xff0c;如果真的面試問到了估計也是一臉懵今日任務&#xff1a;JSON先認識一下JSON和JSONL文件記得之前在面試KIMI的時候&#xff0c;面試官就給我出了JSONL和EXCEL轉換的手撕代碼題&#xff0c;而那個時候&#xff0c;我連什…

Spring框架集成Kakfa的方式

Spring框架集成Kakfa的方式 springboot集成kafka的方式 添加maven依賴 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version> </dependency&g…

【藍橋杯 2024 省 Python B】繳納過路費

【藍橋杯 2024 省 Python B】繳納過路費 藍橋杯專欄&#xff1a;2024 省 Python B 算法競賽&#xff1a;圖論&#xff0c;生成樹&#xff0c;并查集&#xff0c;組合計數&#xff0c;kruskal 最小生成樹&#xff0c;乘法原理 題目鏈接&#xff1a;洛谷 【藍橋杯 2024 省 Python…

個性化導航新體驗:cpolar讓Dashy支持語音控制

文章目錄簡介1. 安裝Dashy2. 安裝cpolar3.配置公網訪問地址4. 固定域名訪問用 cpolar 讓 Dashy 管理個人導航站就是這么簡單&#xff01;三步輕松搞定&#xff1a;在電腦上安裝 Dashy&#xff0c;拖拽添加常用網站&#xff0c;運行 cpolar 生成遠程訪問鏈接。這個方法不僅免費&…

SQL學習記錄

基本的&#xff0c;增、刪&#xff0c;改insert into table_name (列1, 列2,...) VALUES (值1, 值2,....)Delete from 表 where keyvalueupdate 表 set keyvalue,keyvalue where keyvalue查用的最多whereSELECT prod_name, prod_price FROM Products WHERE vend idDLLO1OR ve…