Effective C++ 規則51:編寫 new 和 delete 時需固守常規

1、背景

在 C++ 中,如果你需要為類自定義 new 和 delete,必須遵循一些約定和規則,以確保內存管理的一致性、可維護性和安全性。當我們使用 new 和 delete 操作時,C++ 編譯器會:

  • 調用全局或類特定的 operator new 來分配內存。
  • 調用構造函數(new)或析構函數(delete)。
  • 如果需要,調用全局或類特定的 operator delete 來釋放內存。
    通常,類的內存管理行為依賴于全局版本的 operator new 和 operator delete,但在某些場景下,你可能需要為類定義自定義的版本。

2、自定義new和delete的基本規則

2.1、成對出現

如果為類定義了自定義的 operator new,則必須同時定義對應的 operator delete。

#include <iostream>
#include <cstdlib>class Widget {
public:static void* operator new(size_t size) {std::cout << "Custom operator new: Allocating " << size << " bytes" << std::endl;return std::malloc(size);}static void operator delete(void* ptr) noexcept {std::cout << "Custom operator delete: Freeing memory" << std::endl;std::free(ptr);}
};int main() {/*下面這句會執行兩步:1、調用 operator new(size_t size) 為對象分配內存,在執行這一步時,會將sizeof(Widget)作為參數2、調用對象的構造函數在分配的內存上初始化對象。*/Widget* w = new Widget;delete w;/*Custom operator new: Allocating 1 bytesCustom operator delete: Freeing memory*/return 0;
}

2.2、匹配的內存分配和釋放

  • 任何通過 operator new 分配的內存,必須使用對應的 operator delete 釋放。
  • 避免跨越模塊或庫邊界使用不同版本的 new 和 delete。

2.3、確保異常安全

自定義 operator new 應確保在分配失敗時拋出 std::bad_alloc,而不是返回 nullptr。

#include <new>
#include <iostream>void* operator new(size_t size) {if (size == 0) size = 1; // 確保非零分配void* ptr = std::malloc(size);if (!ptr) throw std::bad_alloc(); // 分配失敗時拋出異常return ptr;
}void operator delete(void* ptr) noexcept {std::free(ptr);
}

2.4、定義 placement new 和 delete

C++ 提供了 placement new,允許你在已分配的內存上構造對象。如果你需要自定義 operator new,也應該定義對應的 placement operator delete。

#include <iostream>class Widget {
public:static void* operator new(size_t size, void* location) {std::cout << "Placement new called" << std::endl;return location;}static void operator delete(void* ptr, void* location) {std::cout << "Placement delete called" << std::endl;// 不釋放內存,因為是 placement new}
};int main() {char buffer[sizeof(Widget)];/*下面這句會執行兩步:1、調用 operator new(size_t size, void* location),在執行這一步時,會將sizeof(Widget)作為第一個參數,buffer作為第2個參數2、調用對象的構造函數在分配的內存上初始化對象。*/Widget* w = new (buffer) Widget;w->~Widget(); // 顯式調用析構函數,輸出Placement new calledreturn 0;
}

3、特殊場景

  • 定制小對象分配器,對于需要頻繁分配和釋放的小對象,可以實現更高效的內存池,這樣做可以優化的原因是,提前做好了分配內存的這一步,在獲取到內存后,只需要再調用構造函數就可以了。
#include <iostream>
#include <vector>class SmallObjectAllocator {
private:std::vector<void*> freeList;size_t objectSize;public:SmallObjectAllocator(size_t objSize) : objectSize(objSize) {}void* allocate() {if (freeList.empty()) {return std::malloc(objectSize);} else {void* ptr = freeList.back();freeList.pop_back();return ptr;}}void deallocate(void* ptr) {freeList.push_back(ptr);}
};class Widget {
public:static SmallObjectAllocator allocator;static void* operator new(size_t size) {return allocator.allocate();}static void operator delete(void* ptr) {allocator.deallocate(ptr);}
};SmallObjectAllocator Widget::allocator(sizeof(Widget));int main() {Widget* w1 = new Widget;Widget* w2 = new Widget;delete w1;delete w2;return 0;
}
  • 優點,減少小對象分配的開銷,提升內存分配性能。

3、總結

在編寫 operator new 和 operator delete 時,應遵循以下關鍵點:

  • 成對定義 new 和 delete,包括 placement 版本。
  • 確保異常安全,分配失敗時拋出 std::bad_alloc。
  • 遵循匹配的內存分配和釋放規則,避免跨模塊不一致。
  • 在需要優化性能時,可實現自定義的內存池或分配器。

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

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

相關文章

JS面相對象小案例:自定義安全數組

在JS中&#xff0c;數組不像其他語言&#xff08;java、python&#xff09;中那樣安全&#xff0c;它具有動態性和弱類型性&#xff0c;切越界訪問沒有具體的報錯&#xff0c;而是返回空&#xff0c;為提升數組的安全性&#xff0c;我們可以自行定義一個安全數組。 一、增加報…

本地大模型編程實戰(02)語義檢索(2)

文章目錄 準備按批次嵌入加載csv文件&#xff0c;分割文檔并嵌入測試嵌入效果總結代碼 上一篇文章&#xff1a; 本地大模型編程實戰(02)語義檢索(1) 詳細介紹了如何使用 langchain 實現語義檢索&#xff0c;為了演示方便&#xff0c;使用的是 langchain 提供的內存數據庫。 在實…

windows平臺intel-vpl編譯

需要先在本機編譯好opencl庫 git clone --recursive https://github.com/KhronosGroup/OpenCL-SDK.git cmake -A x64 -T v143 -D OPENCL_SDK_BUILD_OPENGL_SAMPLESOFF -B OpenCL-SDK\build -S OpenCL-SDKcmake --build OpenCL-SDK\build --config Releasecmake --install O…

Vue 3 30天精進之旅:Day 05 - 事件處理

引言 在前幾天的學習中&#xff0c;我們探討了Vue實例、計算屬性和偵聽器。這些概念為我們搭建了Vue應用的基礎。今天&#xff0c;我們將專注于事件處理&#xff0c;這是交互式Web應用的核心部分。通過學習如何在Vue中處理事件&#xff0c;你將能夠更好地與用戶進行交互&#…

[C語言日寄]exit函數的使用及其拓展

【作者主頁】siy2333 【專欄介紹】?c語言日寄?&#xff1a;這是一個專注于C語言刷題的專欄&#xff0c;精選題目&#xff0c;搭配詳細題解、拓展算法。從基礎語法到復雜算法&#xff0c;題目涉及的知識點全面覆蓋&#xff0c;助力你系統提升。無論你是初學者&#xff0c;還是…

React 中hooks之useSyncExternalStore使用總結

1. 基本概念 useSyncExternalStore 是 React 18 引入的一個 Hook&#xff0c;用于訂閱外部數據源&#xff0c;確保在并發渲染下數據的一致性。它主要用于&#xff1a; 訂閱瀏覽器 API&#xff08;如 window.width&#xff09;訂閱第三方狀態管理庫訂閱任何外部數據源 1.1 基…

激光雷達和相機早期融合

通過外參和內參的標定將激光雷達的點云投影到圖像上。 ? 傳感器標定 首先需要對激光雷達和相機&#xff08;用于獲取 2D 圖像&#xff09;進行外參和內參標定。這是為了確定激光雷達坐標系和相機坐標系之間的轉換關系&#xff0c;包括旋轉和平移。通常采用棋盤格等標定工具&…

Linux--權限

Linux系統的權限管理是保障系統安全的重要機制&#xff0c;以下詳細講解權限相關概念及操作指令&#xff1a; 一、基礎權限機制 1. 權限的三元組&#xff0c;讀&#xff08;r&#xff09;、寫&#xff08;w&#xff09;、執行&#xff08;x&#xff09; 每個文件或目錄有三組…

iic、spi以及uart

何為總線&#xff1f; 連接多個部件的信息傳輸線&#xff0c;是部件共享的傳輸介質 總線的作用&#xff1f; 實現數據傳輸&#xff0c;即模塊之間的通信 總線如何分類&#xff1f; 根據總線連接的外設屬于內部外設還是外部外設將總線可以分為片內總線和片外總線 可分為數…

“破冰”探索兩周年,AI和媒體碰撞出了什么火花?

2022年末&#xff0c;大模型浪潮席卷而來。在“所有行業都值得用AI重塑”的氛圍下&#xff0c;各個行業都受到了影響和沖擊。 其中新聞媒體可以說是受影響最為劇烈的行業。 因為內容的生產方式被重新定義&#xff0c;媒體從業者普遍存在焦慮情緒&#xff1a;擔心錯過新一輪的…

DeepSeek明確學術研究方向效果如何?

明確學術研究方向 在學術寫作中&#xff0c;選擇一個出色的研究主題至關重要&#xff0c;因為它直接關系到論文是否能登上高級別的學術期刊。不少學者在這個過程中走入了誤區&#xff0c;他們往往將大把的時間花在寫作本身&#xff0c;而忽略了對選題的深入思考&#xff0c;這…

WPF實戰案例 | C# WPF實現大學選課系統

WPF實戰案例 | C# WPF實現大學選課系統 一、設計來源1.1 主界面1.2 登錄界面1.3 新增課程界面1.4 修改密碼界面 二、效果和源碼2.1 界面設計&#xff08;XAML&#xff09;2.2 代碼邏輯&#xff08;C#&#xff09; 源碼下載更多優質源碼分享 作者&#xff1a;xcLeigh 文章地址&a…

《 C++ 點滴漫談: 二十四 》深入 C++ 變量與類型的世界:高性能編程的根基

摘要 本文深入探討了 C 中變量與類型的方方面面&#xff0c;包括變量的基本概念、基本與復合數據類型、動態類型與內存管理、類型推導與模板支持&#xff0c;以及類型系統的高級特性。通過全面的理論講解與實際案例分析&#xff0c;展示了 C 類型系統的強大靈活性與實踐價值。…

STM32 GPIO配置 點亮LED燈

本次是基于STM32F407ZET6做一個GPIO配置&#xff0c;實現點燈實驗。 新建文件 LED.c、LED.h文件&#xff0c;將其封裝到Driver文件中。 雙擊Driver文件將LED.c添加進來 編寫頭文件&#xff0c;這里注意需要將Driver頭文件聲明一下。 在LED.c、main.c里面引入頭文件LED.h LED初…

window保存好看的桌面壁紙

1、按下【WINR】快捷鍵調出“運行”窗口&#xff0c;輸入以下命令后回車。 %localappdata%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets 2、依次點擊【查看】【顯示】&#xff0c;勾選【隱藏的項目】&#xff0c;然后按【CtrlA】全部…

TCP 三次握手四次揮手

目錄 TCP 三次握手 1. SYN (Synchronize&#xff1a;同步) 2. SYN-ACK (Synchronize Acknowledge&#xff1a;同步確認) 3. ACK (Acknowledge&#xff1a;確認) 為什么是三次而不是兩次或四次&#xff1f; 三次握手的作用 TCP 四次揮手 第一次揮手&#xff1a;客戶端發送 FIN …

C語言初階牛客網刷題—— HJ34 圖片整理【難度:中等】

1. 題目描述 牛客網在線OJ鏈接 Lily上課時使用字母數字圖片教小朋友們學習英語單詞&#xff0c;每次都需要把這些圖片按照大小&#xff08;ASCII碼值從小到大&#xff09;排列收好。請大家給Lily幫忙&#xff0c;通過C語言解決。 輸入描述&#xff1a;Lily使用的圖片包括 “A…

MVCC底層原理實現

MVCC的實現原理 了解實現原理之前&#xff0c;先理解下面幾個組件的內容 1、 當前讀和快照讀 先普及一下什么是當前讀和快照讀。 當前讀&#xff1a;讀取數據的最新版本&#xff0c;并對數據進行加鎖。 例如&#xff1a;insert、update、delete、select for update、 sele…

python實現http文件服務器訪問下載

//1.py import http.server import socketserver import os import threading import sys# 獲取當前腳本所在的目錄 DIRECTORY os.path.dirname(os.path.abspath(__file__))# 設置服務器的端口 PORT 8000# 自定義Handler&#xff0c;將根目錄設置為腳本所在目錄 class MyHTT…

Cpp::靜態 動態的類型轉換全解析(36)

文章目錄 前言一、C語言中的類型轉換二、為什么C會有四種類型轉換&#xff1f;內置類型 -> 自定義類型自定義類型 -> 內置類型自定義類型 -> 自定義類型隱式類型轉換的坑 三、C強制類型轉換static_castreinterpret_castconst_castdynamic_cast 四、RTTI總結 前言 Hell…